import { assignProjectColors, Button, formatRelativeTime, KvPanel, SectionCard, StatusChip, TextField, useDeckTooltip } from '@overdeck/deck-ui'
import { DataTable, withColumnResizing, withSearch, withSorting, type DataTableColumn } from '@overdeck/deck-ui'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useEffect, useMemo, useState } from 'react'
import { saveProjectColors } from '../../lib/collector-client'
import type { GolivePanelData } from '../../lib/panel-data'
import type { HookInventoryItem, HookInventoryResponse } from '../../lib/collector-types'
import { useCollectorState, useHookInventory, useProjectColors } from '../../lib/collector-queries'
import { CollectorQueryBoundary } from '../shared/CollectorQueryBoundary'
import { RoutingRulesEditor } from './RoutingRulesEditor'

function formatInterval(ms: number): string {
  if (ms % 60_000 === 0) return `${ms / 60_000}m`
  if (ms % 1_000 === 0) return `${ms / 1_000}s`
  return `${ms}ms`
}

export function SettingsContent() {
  const stateQuery = useCollectorState()
  const colorsQuery = useProjectColors()
  const hooksQuery = useHookInventory()
  return (
    <CollectorQueryBoundary query={stateQuery}>
      {(state) => (
        <CollectorQueryBoundary query={colorsQuery}>
          {(colors) => (
            <CollectorQueryBoundary query={hooksQuery}>
              {(hooks) => <SettingsContentBody state={state} colors={colors.projects} hooks={hooks} />}
            </CollectorQueryBoundary>
          )}
        </CollectorQueryBoundary>
      )}
    </CollectorQueryBoundary>
  )
}

function SettingsContentBody({
  state,
  colors,
  hooks,
}: {
  state: import('../../lib/collector-types').StateResponse
  colors: Record<string, string>
  hooks: HookInventoryResponse
}) {
  const queryClient = useQueryClient()
  const scoreboard = state.panels.find((panel) => panel.id === 'scoreboard')?.data as GolivePanelData | undefined
  const projectIds = useMemo(() => scoreboard?.repos.map((repo) => repo.repo) ?? [], [scoreboard])

  const [draft, setDraft] = useState<Record<string, string>>({})
  const [saveMessage, setSaveMessage] = useState<string | null>(null)

  useEffect(() => {
    setDraft(colors)
  }, [colors])

  const assigned = useMemo(
    () => assignProjectColors(projectIds, draft),
    [projectIds, draft],
  )

  const saveMutation = useMutation({
    mutationFn: saveProjectColors,
    onSuccess: async () => {
      await queryClient.invalidateQueries({ queryKey: ['project-colors'] })
      setSaveMessage('Saved project colors')
    },
    onError: () => setSaveMessage('Failed to save project colors'),
  })
  return (
    <div className="flex flex-col gap-3.5">
      <HooksInventoryPanel inventory={hooks} />
      <div className="grid grid-cols-2 gap-3.5">
        <RoutingRulesEditor provider="codex" />
        <RoutingRulesEditor provider="claude" />
        <SectionCard title="Project colors">
        {projectIds.length === 0 ? (
          <div className="text-fg-muted">No projects in scoreboard panel.</div>
        ) : (
          <div className="flex flex-col gap-3">
            {projectIds.map((projectId) => (
              <div key={projectId} className="flex items-center gap-3 text-[12px]">
                <span
                  className="h-4 w-4 rounded-full border border-border"
                  style={{ backgroundColor: assigned[projectId]?.hex }}
                />
                <span className="min-w-[100px] font-medium">{projectId}</span>
                <TextField
                  data-project-color-input={projectId}
                  label={`${projectId} color`}
                  labelMode="hidden"
                  value={draft[projectId] ?? ''}
                  placeholder={assigned[projectId]?.hex}
                  onValueChange={(value) =>
                    setDraft((current) => ({ ...current, [projectId]: value }))
                  }
                />
              </div>
            ))}
            <Button
              size="sm"
              data-save-project-colors
              onClick={() => {
                setSaveMessage(null)
                saveMutation.mutate(draft)
              }}
              className="w-fit"
            >
              Save colors
            </Button>
            {saveMessage ? <div data-save-message className="text-[12px] text-fg-muted">{saveMessage}</div> : null}
          </div>
        )}
        </SectionCard>

        <SectionCard title="Adapter status">
          <KvPanel
            rows={state.adapters.map((adapter) => ({
              label: adapter.id,
              value: `${adapter.stale ? 'stale' : 'ok'} · ${formatInterval(adapter.interval)}`,
              intent: adapter.stale ? 'warn' : 'ok',
            }))}
          />
        </SectionCard>
      </div>
    </div>
  )
}

function PathCell({ value, label }: { value: string | null; label: string }) {
  const tooltip = useDeckTooltip(value ?? 'Not resolved', label)
  return <span className="block max-w-[22rem] truncate font-mono text-[11px] text-fg-muted" {...tooltip}>{value ?? '—'}</span>
}

function EvidenceCell({ row }: { row: HookInventoryItem }) {
  const tooltip = useDeckTooltip(row.lastExecutionAt ?? 'No execution record was available to the collector', row.evidenceSource ?? 'Execution evidence')
  return row.lastExecutionAt
    ? <time dateTime={row.lastExecutionAt} className="tabular-nums text-fg-muted" {...tooltip}>{formatRelativeTime(Date.parse(row.lastExecutionAt))}</time>
    : <span className="text-fg-subtle" {...tooltip}>—</span>
}

const HOOK_COLUMNS: Array<DataTableColumn<HookInventoryItem>> = [
  { id: 'status', header: 'Status', sortable: true, sortValue: (row) => row.status, minWidth: 90, cell: (row) => <StatusChip status={row.status === 'DEAD' ? 'failed' : 'healthy'} label={row.status} /> },
  { id: 'event', header: 'Event', sortable: true, sortValue: (row) => row.event, searchValue: (row) => row.event, minWidth: 135, cell: (row) => <span className="font-semibold text-fg">{row.event}</span> },
  { id: 'matcher', header: 'Matcher', sortable: true, sortValue: (row) => row.matcher ?? '', searchValue: (row) => row.matcher ?? '', minWidth: 150, cell: (row) => row.matcher ?? '—' },
  { id: 'target', header: 'Command path', sortable: true, sortValue: (row) => row.commandPath ?? '', searchValue: (row) => `${row.commandPath ?? ''} ${row.command}`, minWidth: 250, cell: (row) => <PathCell value={row.commandPath} label={row.command} /> },
  { id: 'exists', header: 'Exists', sortable: true, sortValue: (row) => Number(row.exists), minWidth: 75, cell: (row) => row.exists ? 'yes' : 'no' },
  { id: 'executable', header: 'Executable', sortable: true, sortValue: (row) => Number(row.executable), minWidth: 95, cell: (row) => row.executable ? 'yes' : 'no' },
  { id: 'lastExecution', header: 'Last execution', sortable: true, sortValue: (row) => row.lastExecutionAt ? Date.parse(row.lastExecutionAt) : -1, minWidth: 130, cell: (row) => <EvidenceCell row={row} /> },
  { id: 'source', header: 'Scope / source', sortable: true, sortValue: (row) => `${row.scope}:${row.source}`, searchValue: (row) => `${row.scope} ${row.source}`, minWidth: 220, cell: (row) => <PathCell value={row.source} label={`${row.scope} scope`} /> },
]

const HOOK_CAPABILITIES = [withSorting({ defaultSort: { columnId: 'status', direction: 'asc' } }), withSearch({ label: 'Search installed hooks' }), withColumnResizing()]

function HooksInventoryPanel({ inventory }: { inventory: HookInventoryResponse }) {
  const dead = inventory.hooks.filter((hook) => hook.status === 'DEAD').length
  return (
    <SectionCard title="Installed hooks" titleBadge={<StatusChip status={dead > 0 ? 'failed' : 'healthy'} label={dead > 0 ? `${dead} DEAD` : 'ALL ALIVE'} />}>
      <div className="flex flex-col gap-3">
        <p className="text-[11px] text-fg-subtle">
          {inventory.hooks.length} configured hooks from {inventory.sources.length} loaded source{inventory.sources.length === 1 ? '' : 's'}. Last execution is shown only when the collector has direct evidence.
        </p>
        {inventory.sourceErrors.length > 0 ? (
          <p className="text-[11px] text-warning">{inventory.sourceErrors.length} hook source{inventory.sourceErrors.length === 1 ? '' : 's'} could not be read.</p>
        ) : null}
        {inventory.hooks.length === 0 ? <p className="text-sm text-fg-muted">No configured hooks were found in the loaded settings and registries.</p> : (
          <DataTable caption="Installed hook inventory" columns={HOOK_COLUMNS} rows={inventory.hooks} getRowId={(row) => row.id} capabilities={HOOK_CAPABILITIES} />
        )}
      </div>
    </SectionCard>
  )
}
