import { DecisionRow } from '@overdeck/deck-ui'
import { assignProjectColors } from '@overdeck/deck-ui/project-colors'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { postCollectorAction } from '../../lib/action-client'
import { postDecisionAnswer } from '../../lib/decision-answer'
import type { Item } from '../../lib/collector-types'
import { useCollectorItems } from '../../lib/collector-queries'
import { useRequests } from '../../lib/collector-queries'
import type { RequestRow } from '../../lib/request-types'
import { RequestAnswerForm } from '../requests/RequestAnswerForm'
import { useProjectColorOverrides } from '../inbox/project-settings'
import { CollectorPageApp } from '../shared/CollectorPageApp'
import { CollectorQueryBoundary } from '../shared/CollectorQueryBoundary'
import { BrowserApprovalToggle } from './BrowserApprovalToggle'
import { HaltedRunsSection } from './HaltedRunsSection'
import {
  expandedContentForItem,
  factoryDecisionId,
  formatWaitingAge,
  isDecisionQueueItem,
  isFactoryDecisionItem,
  isHaltItem,
  isPermissionItem,
  isOldWaiting,
  optionsForItem,
  projectIdsFromItems,
  sortDecisionItems,
  waitingSinceFor,
} from './decision-mappers'

type RowAnswerState = 'idle' | 'submitting' | 'answered' | 'error'

interface RowState {
  state: RowAnswerState
  error?: string
  answeredSummary?: string
}

function DecisionsList() {
  const itemsQuery = useCollectorItems()
  const requestsQuery = useRequests()
  return (
    <div className="flex flex-col gap-3">
      <BrowserApprovalToggle />
      <CollectorQueryBoundary query={itemsQuery}>{(itemsResponse) => (
        <CollectorQueryBoundary query={requestsQuery}>{(requestsResponse) => (
          <DecisionsListBody items={itemsResponse.items} requests={requestsResponse.requests} />
        )}</CollectorQueryBoundary>
      )}</CollectorQueryBoundary>
    </div>
  )
}

export function DecisionsListBody({ items, requests = [] }: { items: Item[]; requests?: RequestRow[] }) {
  const [expanded, setExpanded] = useState<Record<string, boolean>>({})
  const [freeText, setFreeText] = useState<Record<string, string>>({})
  const [rowState, setRowState] = useState<Record<string, RowState>>({})
  const [optimisticallyAnswered, setOptimisticallyAnswered] = useState<Set<string>>(() => new Set())
  const [requestErrors, setRequestErrors] = useState<Record<string, string>>({})
  const [projectFilter, setProjectFilter] = useState<string | 'all'>('all')
  const [colorOverrides] = useProjectColorOverrides()
  const [now, setNow] = useState(() => Date.now())

  useEffect(() => {
    const id = window.setInterval(() => setNow(Date.now()), 30_000)
    return () => window.clearInterval(id)
  }, [])

  const queueItems = useMemo(
    () => sortDecisionItems(items.filter(isDecisionQueueItem)),
    [items],
  )
  const blockedRequests = useMemo(
    () => requests.filter((request) => request.state === 'blocked_needs_owner' && !optimisticallyAnswered.has(request.id)),
    [requests, optimisticallyAnswered],
  )
  const haltItems = useMemo(() => sortDecisionItems(items.filter(isHaltItem)), [items])
  const projectIds = useMemo(() => projectIdsFromItems(queueItems), [queueItems])
  const colors = useMemo(() => assignProjectColors(projectIds, colorOverrides), [projectIds, colorOverrides])
  const visibleItems = useMemo(
    () => (projectFilter === 'all' ? queueItems : queueItems.filter((item) => item.project === projectFilter)),
    [queueItems, projectFilter],
  )

  const oldestMs = queueItems.reduce((oldest, item) => {
    const since = Date.parse(waitingSinceFor(item))
    return oldest === null || since < oldest ? since : oldest
  }, null as number | null)

  const submitAnswer = useCallback(async (item: Item, choice: string, summary: string) => {
    setRowState((current) => ({ ...current, [item.id]: { state: 'submitting' } }))
    try {
      if (isPermissionItem(item)) {
        await postCollectorAction('permission.answer', {
          args: { requestId: item.id, choice },
          requestedBy: 'deck-web',
        })
      } else if (isFactoryDecisionItem(item)) {
        await postCollectorAction('factory.decision.answer', {
          args: { decisionId: factoryDecisionId(item), choice },
          requestedBy: 'deck-web',
        })
      } else {
        await postDecisionAnswer(item.id, { choice })
      }
      setRowState((current) => ({ ...current, [item.id]: { state: 'answered', answeredSummary: summary } }))
    } catch {
      setRowState((current) => ({
        ...current,
        [item.id]: { state: 'error', error: 'Answer failed — try again' },
      }))
    }
  }, [])

  return (
    <div className="flex flex-col gap-2.5">
      <div className="mb-1.5 flex flex-wrap gap-2">
        <button
          type="button"
          onClick={() => setProjectFilter('all')}
          className={`inline-flex cursor-pointer items-center gap-1.5 rounded-2xl border px-3 py-1 text-[12px] ${
            projectFilter === 'all'
              ? 'border-border-strong bg-[#1d2433] text-[#dbe4ff]'
              : 'border-border bg-surface text-fg-muted'
          }`}
        >
          All {queueItems.length}
        </button>
        {projectIds.map((projectId) => {
          const count = queueItems.filter((item) => item.project === projectId).length
          const hex = colors[projectId]?.hex
          return (
            <button
              key={projectId}
              type="button"
              onClick={() => setProjectFilter(projectId)}
              className={`inline-flex cursor-pointer items-center gap-1.5 rounded-2xl border px-3 py-1 text-[12px] ${
                projectFilter === projectId
                  ? 'border-border-strong bg-[#1d2433] text-[#dbe4ff]'
                  : 'border-border bg-surface text-fg-muted'
              }`}
            >
              {hex ? <span className="h-2 w-2 rounded-full" style={{ backgroundColor: hex }} aria-hidden /> : null}
              {projectId} {count}
            </button>
          )
        })}
      </div>

      <div className="flex items-center gap-3 border-b border-border px-0 py-3">
        <h2 className="text-[15px] font-semibold text-fg">Decisions</h2>
        <span className="text-[12px] text-fg-subtle">
          {queueItems.length + blockedRequests.length} waiting
          {oldestMs !== null ? ` · oldest ${formatWaitingAge(new Date(oldestMs).toISOString(), now).replace('waiting ', '')}` : ''}
          {' · answering unblocks the run immediately'}
        </span>
      </div>

      {blockedRequests.length > 0 ? (
        <section aria-label="Requests waiting for your answer" className="flex flex-col gap-3 border-b border-border pb-4">
          <h3 className="text-sm font-semibold text-fg">Requests waiting for your answer</h3>
          {blockedRequests.map((request) => (
            <article key={request.id} className="flex flex-col gap-2 rounded-md border border-warning/40 bg-warning/10 p-3">
              <p className="text-sm font-semibold text-fg">{request.title}</p>
              <p className="text-sm text-fg-muted">{request.detail ?? 'Owner input needed — no further detail recorded'}</p>
              <RequestAnswerForm
                request={request}
                error={requestErrors[request.id]}
                onOptimistic={() => setOptimisticallyAnswered((current) => new Set(current).add(request.id))}
                onSuccess={() => setRequestErrors((current) => { const { [request.id]: _, ...rest } = current; return rest })}
                onError={(message) => {
                  setOptimisticallyAnswered((current) => { const next = new Set(current); next.delete(request.id); return next })
                  setRequestErrors((current) => ({ ...current, [request.id]: message }))
                }}
              />
            </article>
          ))}
        </section>
      ) : null}
      {visibleItems.length === 0 && blockedRequests.length === 0 ? (
        <p className="py-8 text-center text-[13px] text-fg-muted" data-decisions-empty>
          No decisions waiting — inbox zero for human-in-the-loop.
        </p>
      ) : (
        visibleItems.map((item) => {
          const waitingSince = waitingSinceFor(item)
          const expandedContent = expandedContentForItem(item)
          const answer = rowState[item.id] ?? { state: 'idle' as const }
          const projectHex = item.project ? colors[item.project]?.hex : undefined
          return (
            <DecisionRow
              key={item.id}
              itemId={item.id}
              projectName={item.project}
              projectColor={projectHex}
              title={item.title}
              meta={item.detail}
              waitingAgeLabel={formatWaitingAge(waitingSince, now)}
              isOld={isOldWaiting(waitingSince, now)}
              expanded={expanded[item.id] ?? false}
              onToggleExpand={() => setExpanded((current) => ({ ...current, [item.id]: !current[item.id] }))}
              question={expandedContent.question}
              dataBlock={expandedContent.dataBlock}
              contextKvs={expandedContent.contextKvs}
              journalRef={expandedContent.journalRef}
              options={optionsForItem(item)}
              allowFreeText={expandedContent.allowFreeText}
              freeTextValue={freeText[item.id] ?? ''}
              onFreeTextChange={(value) => setFreeText((current) => ({ ...current, [item.id]: value }))}
              answerState={answer.state}
              errorMessage={answer.error}
              answeredSummary={answer.answeredSummary}
              onSelectOption={(option) => void submitAnswer(item, option.choice, option.label)}
              onSubmitFreeText={() => {
                const text = (freeText[item.id] ?? '').trim()
                if (!text) return
                void submitAnswer(item, text, text)
              }}
            />
          )
        })
      )}
      <HaltedRunsSection items={haltItems} />
    </div>
  )
}

export function DecisionsApp() {
  return (
    <CollectorPageApp>
      <DecisionsList />
    </CollectorPageApp>
  )
}
