import { useRef, useState, type FormEvent, type JSX } from 'react'
import { Button, Checkbox, DetailDrawer, Select, TextArea, TextField } from '@overdeck/deck-ui'
import { CollectorHttpError } from '../../lib/collector-client'
import { useFileIncident, useIncidentOptions } from '../../lib/collector-queries'
import type { Incident, IncidentPriority } from '../../lib/incident-types'
import { accountAuthorityOptions, cliAuthorityOptions, effortAuthorityOptions, modelAuthorityOptions } from './incident-view'

const PRIORITIES = [
  { value: 'P0', label: 'P0 · Critical' },
  { value: 'P1', label: 'P1 · High' },
  { value: 'P2', label: 'P2 · Normal' },
  { value: 'P3', label: 'P3 · Low' },
] as const

interface Draft {
  title: string
  description: string
  incidentType: string
  cli: string
  model: string
  reasoningEffort: string
  account: string
  unsafe: boolean
  priority: IncidentPriority
}

const EMPTY_DRAFT: Draft = {
  title: '', description: '', incidentType: 'unresolved', cli: '', model: '', reasoningEffort: '', account: '', unsafe: false, priority: 'P2',
}

function filingError(error: unknown): string {
  if (error instanceof CollectorHttpError && error.status === 409) return 'This filing request conflicts with an existing incident. Refresh and try again.'
  if (error instanceof CollectorHttpError && error.status === 503) return 'Incident dispatch options are unavailable. Your input has been kept.'
  return 'The incident could not be filed. Your input has been kept.'
}

export function FileIncidentForm({ open, onClose, onFiled }: { open: boolean; onClose(): void; onFiled(incident: Incident): void }): JSX.Element | null {
  const [draft, setDraft] = useState<Draft>(EMPTY_DRAFT)
  const [errors, setErrors] = useState<Partial<Record<keyof Draft, string>>>({})
  const requestId = useRef(crypto.randomUUID())
  const mutation = useFileIncident()
  const optionsQuery = useIncidentOptions()
  const authorityClis = optionsQuery.data?.clis ?? []
  const authorityTypes = optionsQuery.data?.types ?? []
  const typeOptions = [{ value: 'unresolved', label: 'Unresolved' }, ...authorityTypes.map((type) => ({ value: type.id, label: type.title }))]
  const selectedCli = authorityClis.find((cli) => cli.id === draft.cli)
  const selectedModel = selectedCli?.models.find((model) => model.id === draft.model)
  const unsafeSupported = selectedCli?.permissionModes.includes('unsafe') === true

  if (!open) return null

  function set<K extends keyof Draft>(key: K, value: Draft[K]): void {
    setDraft((current) => {
      const next = { ...current, [key]: value }
      if (key === 'cli') {
        const cli = authorityClis.find((entry) => entry.id === value)
        if (!cli?.models.some((model) => model.id === next.model)) next.model = ''
        const model = cli?.models.find((entry) => entry.id === next.model)
        if (!model?.efforts.includes(next.reasoningEffort)) next.reasoningEffort = ''
        if (!cli?.accounts.some((account) => account.ready && account.slug === next.account)) next.account = ''
        if (!cli?.permissionModes.includes('unsafe')) next.unsafe = false
      }
      if (key === 'model') {
        const model = selectedCli?.models.find((entry) => entry.id === value)
        if (!model?.efforts.includes(next.reasoningEffort)) next.reasoningEffort = ''
      }
      return next
    })
    setErrors((current) => ({ ...current, [key]: undefined }))
  }

  function submit(event: FormEvent<HTMLFormElement>): void {
    event.preventDefault()
    const nextErrors: typeof errors = {}
    if (!draft.title.trim()) nextErrors.title = 'Required.'
    if (!draft.description.trim()) nextErrors.description = 'Required.'
    if (draft.title.trim().length > 160) nextErrors.title = 'Use 160 characters or fewer.'
    if (draft.description.trim().length > 20_000) nextErrors.description = 'Use 20,000 characters or fewer.'
    if (!selectedCli) nextErrors.cli = 'Required.'
    else {
      if (!selectedModel) nextErrors.model = 'Required.'
      else if (!selectedModel.efforts.includes(draft.reasoningEffort)) nextErrors.reasoningEffort = 'Required.'
      if (!selectedCli.accounts.some((account) => account.ready && account.slug === draft.account)) nextErrors.account = 'Required.'
      if (draft.unsafe && !unsafeSupported) nextErrors.unsafe = 'Unsafe mode is not authorized for this CLI.'
    }
    if (Object.keys(nextErrors).length > 0) {
      setErrors(nextErrors)
      const first = Object.keys(nextErrors)[0]
      requestAnimationFrame(() => document.querySelector<HTMLElement>(`[name="${first}"]`)?.focus())
      return
    }
    const { incidentType, ...request } = draft
    mutation.mutate({ requestId: requestId.current, ...request, ...(incidentType === 'unresolved' ? {} : { incidentType }), title: draft.title.trim(), description: draft.description.trim() }, {
      onSuccess: ({ incident }) => {
        setDraft(EMPTY_DRAFT)
        requestId.current = crypto.randomUUID()
        onClose()
        onFiled(incident)
      },
    })
  }

  const authorityUnavailable = optionsQuery.isError || (!optionsQuery.isPending && authorityClis.length === 0)
  return (
    <DetailDrawer eyebrow="New incident" title="File incident" titleId="file-incident-title" onClose={onClose}>
      <form className="mt-4 space-y-3" onSubmit={submit} noValidate>
        <TextField name="title" label="Title" value={draft.title} onValueChange={(value) => set('title', value)} maxLength={161} error={errors.title} />
        <TextArea name="description" label="Description" value={draft.description} onValueChange={(value) => set('description', value)} maxLength={20_001} error={errors.description} />
        <Select name="incidentType" label="Type" value={draft.incidentType} options={typeOptions} onValueChange={(value) => set('incidentType', value)} disabled={authorityUnavailable} />
        <Select name="cli" label="CLI" value={draft.cli} options={cliAuthorityOptions(authorityClis)} onValueChange={(value) => set('cli', value)} error={errors.cli} disabled={authorityUnavailable} />
        <Select name="model" label="Model" value={draft.model} options={modelAuthorityOptions(selectedCli)} onValueChange={(value) => set('model', value)} error={errors.model} disabled={!selectedCli} />
        <Select name="reasoningEffort" label="Reasoning effort" value={draft.reasoningEffort} options={effortAuthorityOptions(selectedCli, draft.model)} onValueChange={(value) => set('reasoningEffort', value)} error={errors.reasoningEffort} disabled={!selectedModel} />
        <Select name="account" label="Account" value={draft.account} options={accountAuthorityOptions(selectedCli)} onValueChange={(value) => set('account', value)} error={errors.account} disabled={!selectedCli} />
        <Checkbox name="unsafe" label="Unsafe" checked={draft.unsafe} onCheckedChange={(value) => set('unsafe', value)} disabled={!unsafeSupported} error={errors.unsafe} hint={unsafeSupported ? 'Allows this attempt to use the registered unsafe permission mode.' : 'Unavailable unless the selected CLI authoritatively declares unsafe mode.'} />
        <Select name="priority" label="Priority" value={draft.priority} options={PRIORITIES} onValueChange={(value) => set('priority', value as IncidentPriority)} />
        {authorityUnavailable && <p className="text-sm text-danger" role="alert">Incident dispatch options are unavailable. Filing is disabled.</p>}
        {mutation.isError && <p className="text-sm text-danger" aria-live="polite">{filingError(mutation.error)}</p>}
        <Button type="submit" disabled={mutation.isPending || authorityUnavailable}>{mutation.isPending ? 'Filing…' : 'File and dispatch'}</Button>
      </form>
    </DetailDrawer>
  )
}
