import { define } from '@because/forge'
import type { App, LogLine as LogLineType } from '../../shared/types'
import { getLogDates, getLogsForDate } from '../api'
import { LogLine, LogsContainer, LogTime, Section, SectionTitle } from '../styles'
import { theme } from '../themes'
import { update } from '../update'

type LogsState = {
  dates: string[]
  historicalLogs: string[]
  loadingDates: boolean
  loadingLogs: boolean
  searchFilter: string
  selectedDate: string
}

const logsState = new Map<string, LogsState>()

let currentApp: App | null = null

const getState = (appName: string): LogsState => {
  if (!logsState.has(appName)) {
    logsState.set(appName, {
      dates: [],
      historicalLogs: [],
      loadingDates: false,
      loadingLogs: false,
      searchFilter: '',
      selectedDate: 'live',
    })
  }
  return logsState.get(appName)!
}

const LogsHeader = define('LogsHeader', {
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'space-between',
  gap: 12,
  marginBottom: 12,
})

const LogsControls = define('LogsControls', {
  display: 'flex',
  alignItems: 'center',
  gap: 8,
})

const SmallSelect = define('SmallSelect', {
  base: 'select',
  padding: '4px 8px',
  background: theme('colors-bgSubtle'),
  border: `1px solid ${theme('colors-border')}`,
  borderRadius: '4px',
  color: theme('colors-text'),
  fontSize: 12,
  cursor: 'pointer',
  selectors: {
    '&:focus': {
      outline: 'none',
      borderColor: theme('colors-primary'),
    },
  },
})

const SmallInput = define('SmallInput', {
  base: 'input',
  padding: '4px 8px',
  background: theme('colors-bgSubtle'),
  border: `1px solid ${theme('colors-border')}`,
  borderRadius: '4px',
  color: theme('colors-text'),
  fontSize: 12,
  width: 120,
  selectors: {
    '&:focus': {
      outline: 'none',
      borderColor: theme('colors-primary'),
    },
    '&::placeholder': {
      color: theme('colors-textFaint'),
    },
  },
})

function filterLogs<T extends string | LogLineType>(
  logs: T[],
  filter: string,
  getText: (log: T) => string
): T[] {
  if (!filter) return logs
  const lower = filter.toLowerCase()
  return logs.filter(log => getText(log).toLowerCase().includes(lower))
}

function LogsContent() {
  if (!currentApp) return null

  const state = getState(currentApp.name)
  const isLive = state.selectedDate === 'live'
  const filteredLiveLogs = filterLogs(currentApp.logs ?? [], state.searchFilter, l => l.text)
  const filteredHistoricalLogs = filterLogs(state.historicalLogs, state.searchFilter, l => l)

  return (
    <>
      {state.loadingLogs && (
        <LogLine>
          <LogTime>--:--:--</LogTime>
          <span style={{ color: theme('colors-textFaint') }}>Loading...</span>
        </LogLine>
      )}
      {!state.loadingLogs && isLive && (
        filteredLiveLogs.length ? (
          filteredLiveLogs.map((line, i) => (
            <LogLine key={i}>
              <LogTime>{new Date(line.time).toLocaleTimeString()}</LogTime>
              <span>{line.text}</span>
            </LogLine>
          ))
        ) : (
          <LogLine>
            <LogTime>--:--:--</LogTime>
            <span style={{ color: theme('colors-textFaint') }}>
              {state.searchFilter ? 'No matching logs' : 'No logs yet'}
            </span>
          </LogLine>
        )
      )}
      {!state.loadingLogs && !isLive && (
        filteredHistoricalLogs.length ? (
          filteredHistoricalLogs.map((line, i) => (
            <LogLine key={i}>
              <span style={{ color: theme('colors-textFaintest'), marginRight: 12 }}>
                {line.match(/^\[([^\]]+)\]/)?.[1]?.split('T')[1]?.slice(0, 8) ?? '--:--:--'}
              </span>
              <span>{line.replace(/^\[[^\]]+\] \[[^\]]+\] /, '')}</span>
            </LogLine>
          ))
        ) : (
          <LogLine>
            <LogTime>--:--:--</LogTime>
            <span style={{ color: theme('colors-textFaint') }}>
              {state.searchFilter ? 'No matching logs' : 'No logs for this date'}
            </span>
          </LogLine>
        )
      )}
    </>
  )
}

const updateLogsContent = () =>
  update('#logs-content', <LogsContent />)

async function loadDates(appName: string) {
  const state = getState(appName)
  if (state.loadingDates || state.dates.length > 0) return

  state.loadingDates = true

  try {
    const dates = await getLogDates(appName)
    state.dates = dates
  } catch (e) {
    console.error('Failed to load log dates:', e)
  } finally {
    state.loadingDates = false
  }
}

async function loadHistoricalLogs(appName: string, date: string) {
  const state = getState(appName)
  state.loadingLogs = true
  updateLogsContent()

  try {
    const logs = await getLogsForDate(appName, date)
    state.historicalLogs = logs
  } catch (e) {
    console.error('Failed to load logs:', e)
    state.historicalLogs = []
  } finally {
    state.loadingLogs = false
    updateLogsContent()
  }
}

function handleDateChange(appName: string, date: string) {
  const state = getState(appName)
  state.selectedDate = date
  state.historicalLogs = []

  if (date !== 'live') {
    loadHistoricalLogs(appName, date)
  } else {
    updateLogsContent()
  }
}

function handleSearchChange(appName: string, value: string) {
  const state = getState(appName)
  state.searchFilter = value
  updateLogsContent()
}

export function LogsSection({ app }: { app: App }) {
  currentApp = app
  const state = getState(app.name)

  // Load dates on first render
  if (state.dates.length === 0 && !state.loadingDates) {
    loadDates(app.name)
  }

  return (
    <Section>
      <LogsHeader>
        <SectionTitle style={{ marginBottom: 0 }}>Logs</SectionTitle>
        <LogsControls>
          <SmallInput
            id="logs-filter"
            type="text"
            placeholder="Filter..."
            value={state.searchFilter}
            onInput={(e: Event) => handleSearchChange(app.name, (e.target as HTMLInputElement).value)}
          />
          <SmallSelect
            id="logs-date"
            value={state.selectedDate}
            onChange={(e: Event) => handleDateChange(app.name, (e.target as HTMLSelectElement).value)}
          >
            <option value="live">Live</option>
            {state.dates.map(date => (
              <option key={date} value={date}>{date}</option>
            ))}
          </SmallSelect>
        </LogsControls>
      </LogsHeader>
      <LogsContainer id="logs-content">
        <LogsContent />
      </LogsContainer>
    </Section>
  )
}
