import { useMemo, useState, type JSX } from 'react'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import {
  DataTable,
  withColumnResizing,
  withSearch,
  withSorting,
  type DataTableColumn,
  type DataTableSort,
} from '@overdeck/deck-ui'
import {
  ActionsMenu,
  Button,
  DataCoveragePanel,
  DeckTooltipLayer,
  FilterInput,
  Select,
  SectionCard,
  StatusChip,
  useDeckTooltip,
} from '@overdeck/deck-ui'
import { CollectorHttpError, deleteIncident, dispatchIncident, stopIncident } from '../../lib/collector-client'
import { collectorQueryKeys } from '../../lib/collector-queries'
import { useIncident, useIncidents } from '../../lib/collector-queries'
import type { Incident } from '../../lib/incident-types'
import { DeckPageSkeleton } from '../shared/DeckPageSkeleton'
import { IncidentDetailDrawer } from './IncidentDetailDrawer'
import { FileIncidentForm } from './FileIncidentForm'
import {
  EM_DASH,
  INCIDENT_STATE_LABEL,
  cliAndModel,
  displayOrDash,
  incidentEpoch,
  incidentStatusToken,
  incidentTimestamp,
  dispatchFailure,
  listCoverageGaps,
  matchesIncidentFilter,
  priorityRank,
  sortIncidents,
} from './incident-view'

function TimeCell({ value }: { value: string | null }): JSX.Element {
  const timestamp = incidentTimestamp(value)
  const tooltip = useDeckTooltip(timestamp.absolute ?? '')
  if (timestamp.absolute === null) return <>{EM_DASH}</>
  return <span {...tooltip}>{timestamp.relative}</span>
}

function ToolsIcon(): JSX.Element {
  return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M14.7 6.3a4 4 0 0 0-5-5l2.1 2.1-2.4 2.4-2.1-2.1a4 4 0 0 0 5 5l7.1 7.1a2 2 0 0 1-2.8 2.8l-7.1-7.1"/><path d="m5 19 4-4"/></svg>
}

function incidentColumns(onOpen: (incident: Incident) => void, onStop: (incident: Incident) => void, onDelete: (incident: Incident) => void, pendingId: string | null): DataTableColumn<Incident>[] {
  return [
    { id: 'incident', header: 'Incident', sortable: true, sortValue: (row) => row.title.toLowerCase(), searchValue: (row) => `${row.title} ${row.id}`, minWidth: 220, cell: (row) => <span className="block truncate font-medium text-fg">{row.title}</span> },
    { id: 'priority', header: 'Priority', sortable: true, sortValue: (row) => priorityRank(row.priority) ?? Number.MAX_SAFE_INTEGER, searchValue: (row) => row.priority ?? '', minWidth: 90, cell: (row) => row.priority ?? EM_DASH },
    { id: 'status', header: 'Status', sortable: true, sortValue: (row) => INCIDENT_STATE_LABEL[row.state].toLowerCase(), searchValue: (row) => INCIDENT_STATE_LABEL[row.state], minWidth: 110, cell: (row) => <StatusChip status={incidentStatusToken(row.state)} label={INCIDENT_STATE_LABEL[row.state]} /> },
    { id: 'agent', header: 'CLI / model', sortable: true, sortValue: (row) => cliAndModel(row).toLowerCase(), searchValue: (row) => cliAndModel(row), minWidth: 140, cell: cliAndModel },
    { id: 'account', header: 'Account', sortable: true, sortValue: (row) => (row.dispatch.account ?? '').toLowerCase(), searchValue: (row) => row.dispatch.account ?? '', minWidth: 110, cell: (row) => displayOrDash(row.dispatch.account) },
    { id: 'opened', header: 'Opened', sortable: true, sortValue: (row) => incidentEpoch(row.createdAt) ?? -1, searchValue: (row) => row.createdAt ?? '', minWidth: 110, cell: (row) => <TimeCell value={row.createdAt} /> },
    { id: 'updated', header: 'Updated', sortable: true, sortValue: (row) => incidentEpoch(row.updatedAt) ?? -1, searchValue: (row) => row.updatedAt ?? '', minWidth: 110, cell: (row) => <TimeCell value={row.updatedAt} /> },
    { id: 'actions', header: '', minWidth: 56, cell: (row) => pendingId !== null ? <span className="text-sm text-fg-muted">{pendingId === row.id ? 'Working…' : 'Action pending'}</span> : <ActionsMenu label={`Actions for ${row.id}`} triggerIcon={<ToolsIcon />} items={[{ label: 'View details', onSelect: () => onOpen(row) }, row.active ? { label: 'Stop work', tone: 'danger', dividerAbove: true, onSelect: () => { if (window.confirm(`Stop active work for ${row.id}?`)) onStop(row) } } : { label: 'Delete incident', tone: 'danger', dividerAbove: true, onSelect: () => { if (window.confirm(`Permanently delete ${row.id}?`)) onDelete(row) } }]} /> },
  ]
}

function storeErrorMessage(error: unknown): string {
  if (error instanceof CollectorHttpError) {
    if (error.status === 503) {
      return 'The incident store is unreachable, so incidents cannot be listed. Existing incidents are unaffected.'
    }
    if (error.status === 404) {
      return 'This collector has no incident store configured, so there is nothing to list.'
    }
  }
  return 'Incidents could not be loaded.'
}

export function IncidentsContent(): JSX.Element {
  const [filter, setFilter] = useState('')
  const [priority, setPriority] = useState<'' | Incident['priority']>('')
  const [filing, setFiling] = useState(false)
  const [sort, setSort] = useState<DataTableSort | null>(null)
  const [selected, setSelected] = useState<Incident | null>(null)
  const queryClient = useQueryClient()

  const incidentsQuery = useIncidents({ scope: 'all' })
  const detailQuery = useIncident(selected?.id ?? null)
  const dispatchMutation = useMutation({
    mutationFn: ({ incidentId, options }: { incidentId: string; options?: { withoutBrief?: boolean } }) =>
      dispatchIncident(incidentId, options ?? {}),
    onSuccess: async (incident) => {
      setSelected(incident)
      queryClient.setQueryData(collectorQueryKeys.incident(incident.id), incident)
      await queryClient.invalidateQueries({ queryKey: ['collector-incidents'] })
    },
  })

  const lifecycleMutation = useMutation({
    mutationFn: async ({ incident, action }: { incident: Incident; action: 'stop' | 'delete' }) => {
      if (action === 'stop') return { action, incident: await stopIncident(incident.id) } as const
      await deleteIncident(incident.id)
      return { action, incident } as const
    },
    onSuccess: async (result) => {
      if (result.action === 'delete') {
        if (selected?.id === result.incident.id) setSelected(null)
        queryClient.removeQueries({ queryKey: collectorQueryKeys.incident(result.incident.id) })
      } else {
        setSelected((current) => current?.id === result.incident.id ? result.incident : current)
        queryClient.setQueryData(collectorQueryKeys.incident(result.incident.id), result.incident)
      }
      await queryClient.invalidateQueries({ queryKey: ['collector-incidents'] })
    },
  })

  const all = useMemo(() => incidentsQuery.data?.pages.flatMap((page) => page.incidents) ?? [], [incidentsQuery.data])
  const matching = useMemo(
    () => all.filter((incident) => matchesIncidentFilter(incident, filter) && (priority === '' || incident.priority === priority)),
    [all, filter, priority],
  )
  const active = useMemo(() => sortIncidents(matching.filter((i) => i.active)), [matching])
  const resolved = useMemo(() => sortIncidents(matching.filter((i) => !i.active)), [matching])
  const columns = useMemo(() => incidentColumns(setSelected, (incident) => lifecycleMutation.mutate({ incident, action: 'stop' }), (incident) => lifecycleMutation.mutate({ incident, action: 'delete' }), lifecycleMutation.isPending ? lifecycleMutation.variables?.incident.id ?? null : null), [lifecycleMutation.isPending, lifecycleMutation.variables])
  const capabilities = useMemo(() => [withSorting({ sort, onSortChange: setSort, manual: false }), withSearch({ label: 'Search incidents' }), withColumnResizing()], [sort])

  if (incidentsQuery.isPending) return <DeckPageSkeleton />

  if (incidentsQuery.isError) {
    return (
      <SectionCard title="Incidents">
        <p className="text-sm text-danger" data-testid="incidents-store-error">
          {storeErrorMessage(incidentsQuery.error)}
        </p>
        <div className="mt-3">
          <Button variant="outline" tone="neutral" size="sm" onClick={() => incidentsQuery.refetch()}>
            Retry
          </Button>
        </div>
      </SectionCard>
    )
  }

  const coverage = incidentsQuery.data?.pages.find((page) => page.coverage.stale)?.coverage
    ?? incidentsQuery.data?.pages[0]?.coverage
  const gaps = listCoverageGaps(coverage?.detail, coverage?.stale === true)

  return (
    <div className="space-y-4">
      <div className="flex flex-wrap items-end justify-between gap-3">
        <div className="flex flex-wrap items-end gap-3">
          <FilterInput
            label="Filter incidents"
            value={filter}
            onChange={setFilter}
            placeholder="Title, id, CLI, account…"
          />
          <Select
            label="Priority filter"
            value={priority ?? ''}
            options={[
              { value: '', label: 'All priorities' },
              { value: 'P0', label: 'P0' }, { value: 'P1', label: 'P1' },
              { value: 'P2', label: 'P2' }, { value: 'P3', label: 'P3' },
            ]}
            onValueChange={(value) => setPriority(value as '' | Incident['priority'])}
          />
        </div>
        <Button onClick={() => setFiling(true)}>File incident</Button>
        {gaps.length > 0 && <DataCoveragePanel gaps={gaps} />}
      </div>

      {lifecycleMutation.isError && (
        <p role="alert" className="text-sm text-danger">The incident action failed. The incident was not removed from this view.</p>
      )}

      <SectionCard title="Active">
        {active.length === 0 ? (
          <p className="text-sm text-fg-muted" data-testid="incidents-active-empty">
            No active incidents.
          </p>
        ) : (
          <div data-testid="incidents-active-table">
          <DataTable
            caption="Active incidents"
            columns={columns}
            rows={active}
            getRowId={(row) => row.id}
            capabilities={capabilities}
          />
          </div>
        )}
      </SectionCard>

      <details data-testid="incidents-resolved-section">
        <summary className="flex min-h-11 cursor-pointer list-none items-center rounded-md border border-border px-3 text-xs font-semibold text-fg-muted hover:bg-surface-raised focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent">
          Resolved ({resolved.length})
        </summary>
        <div className="mt-3">
          {resolved.length === 0 ? (
            <p className="text-sm text-fg-muted">No resolved incidents.</p>
          ) : (
            <div data-testid="incidents-resolved-table">
            <DataTable caption="Resolved incidents" columns={columns} rows={resolved} getRowId={(row) => row.id} capabilities={capabilities} />
            </div>
          )}
        </div>
      </details>

      {incidentsQuery.hasNextPage && (
        <div className="flex justify-center">
          <Button
            variant="outline"
            tone="neutral"
            size="sm"
            disabled={incidentsQuery.isFetchingNextPage}
            onClick={() => incidentsQuery.fetchNextPage()}
          >
            {incidentsQuery.isFetchingNextPage ? 'Loading…' : 'Load more incidents'}
          </Button>
        </div>
      )}

      <FileIncidentForm open={filing} onClose={() => setFiling(false)} onFiled={setSelected} />

      {selected !== null && (
        <IncidentDetailDrawer
          summary={selected}
          detail={detailQuery.data?.id === selected.id ? detailQuery.data : undefined}
          detailError={detailQuery.isError}
          dispatching={dispatchMutation.isPending}
          dispatchFailure={dispatchMutation.isError ? dispatchFailure(dispatchMutation.error) : null}
          onDispatch={(id, options) => dispatchMutation.mutate({ incidentId: id, options })}
          onClose={() => setSelected(null)}
        />
      )}

      <DeckTooltipLayer />
    </div>
  )
}
