'use client'

// Manual process-step matching (KAR-845): the A3 audit table becomes an
// editor. Every ALT step row gets a NEU dropdown; changed or previously
// manual pairs are pinned and sent to recompareComparison, which re-runs the
// deterministic engine with match_method='manual' for the pins.
// tdd-guard:skip — interactive shell; pinning logic lives in
// internal/matcher.ts (matchStepsWithPins, unit-tested).

import { useMemo, useState, useTransition } from 'react'
import { useRouter } from 'next/navigation'
import { RotateCcw } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Pill } from '@/components/qaf-differences/qaf-section'
import { recompareComparison } from '@/app/qaf-differences/actions'

export interface MatchRow {
  id: string
  alt_step_id: string | null
  neu_step_id: string | null
  match_status: string
  confidence_score: number | null
  match_method: string | null
  requires_review: boolean | null
}

export interface StepOption {
  id: string
  file_id: string
  process_name: string | null
  position_number: string | null
}

const pctFmt = new Intl.NumberFormat('de-DE', { style: 'percent', maximumFractionDigits: 1 })

function label(s: StepOption | undefined): string {
  if (!s) return '—'
  return [s.position_number, s.process_name].filter(Boolean).join(' ') || '(unbenannt)'
}

interface Props {
  comparisonId: string
  matches: MatchRow[]
  steps: StepOption[]
  neuFileId: string
}

export default function QafStepMatching({ comparisonId, matches, steps, neuFileId }: Props) {
  const router = useRouter()
  const [pending, startTransition] = useTransition()
  const [error, setError] = useState<string | null>(null)
  const stepById = useMemo(() => new Map(steps.map((s) => [s.id, s])), [steps])
  const neuOptions = useMemo(() => steps.filter((s) => s.file_id === neuFileId), [steps, neuFileId])

  // Rows with an ALT step are editable; assignment state starts at the DB match.
  const altRows = useMemo(() => matches.filter((m) => m.alt_step_id !== null), [matches])
  const [assigned, setAssigned] = useState<Record<string, string | null>>(() =>
    Object.fromEntries(altRows.map((m) => [m.id, m.neu_step_id])),
  )
  const [touched, setTouched] = useState<Set<string>>(new Set())

  const duplicates = useMemo(() => {
    const seen = new Map<string, number>()
    for (const v of Object.values(assigned)) if (v) seen.set(v, (seen.get(v) ?? 0) + 1)
    return new Set([...seen.entries()].filter(([, n]) => n > 1).map(([id]) => id))
  }, [assigned])

  const hasChanges = touched.size > 0
  const assignedIds = useMemo(() => new Set(Object.values(assigned).filter(Boolean) as string[]), [assigned])
  const unmatchedNeu = neuOptions.filter((s) => !assignedIds.has(s.id))

  function apply() {
    setError(null)
    if (duplicates.size > 0) {
      return setError('Ein NEU-Schritt ist mehrfach zugeordnet — jede Zuordnung darf nur einmal vorkommen.')
    }
    // Pins = previously manual pairs (kept) + user-touched pairs. A null
    // neuStepId is a NEGATIVE pin: the step stays unmatched (kein Match).
    const pins = altRows
      .filter((m) => m.match_method === 'manual' || touched.has(m.id))
      .map((m) => ({ altStepId: m.alt_step_id as string, neuStepId: assigned[m.id] ?? null }))
    startTransition(async () => {
      const res = await recompareComparison(comparisonId, { pins })
      if (res.ok) {
        router.refresh()
      } else {
        setError(res.error)
      }
    })
  }

  return (
    <div className="space-y-2">
      <div className="overflow-x-auto">
        <table className="w-full text-sm">
          <thead>
            <tr className="border-b border-border text-left text-muted-foreground">
              <th className="py-2 pr-3 font-medium">Schritt (ALT)</th>
              <th className="py-2 pr-3 font-medium">zugeordnet zu (NEU)</th>
              <th className="py-2 pr-3 text-right font-medium">Konfidenz</th>
              <th className="py-2 font-medium">Methode</th>
            </tr>
          </thead>
          <tbody>
            {matches
              .filter((m) => m.alt_step_id === null && m.neu_step_id !== null)
              .map((m) => (
                <tr key={m.id} className="border-b border-border/60 opacity-70">
                  <td className="py-1.5 pr-3 text-muted-foreground">— (nur in NEU)</td>
                  <td className="py-1.5 pr-3 text-foreground">{label(stepById.get(m.neu_step_id as string))}</td>
                  <td className="py-1.5 pr-3 text-right tabular-nums">—</td>
                  <td className="py-1.5">
                    <Pill label="neu" className="bg-primary-tint text-foreground" />
                  </td>
                </tr>
              ))}
            {altRows.map((m) => {
              const isManual = m.match_method === 'manual' || touched.has(m.id)
              const value = assigned[m.id] ?? ''
              return (
                <tr key={m.id} className={`border-b border-border/60 ${touched.has(m.id) ? 'bg-muted/40' : ''}`}>
                  <td className="py-1.5 pr-3 text-foreground">{label(stepById.get(m.alt_step_id as string))}</td>
                  <td className="py-1.5 pr-3">
                    <select
                      value={value}
                      disabled={pending}
                      onChange={(e) => {
                        setAssigned((a) => ({ ...a, [m.id]: e.target.value || null }))
                        setTouched((t) => new Set(t).add(m.id))
                      }}
                      className={`w-full max-w-72 rounded border bg-background px-2 py-1 text-sm text-foreground ${
                        value && duplicates.has(value) ? 'border-destructive' : 'border-border'
                      }`}
                    >
                      <option value="">— kein Match (Schritt entfallen) —</option>
                      {neuOptions.map((s) => (
                        <option key={s.id} value={s.id}>
                          {label(s)}
                        </option>
                      ))}
                    </select>
                  </td>
                  <td className="py-1.5 pr-3 text-right tabular-nums">
                    {isManual ? '100 %' : m.confidence_score === null ? '—' : pctFmt.format(m.confidence_score)}
                  </td>
                  <td className="py-1.5">
                    {isManual ? (
                      <Pill label="manuell" className="bg-primary-soft text-foreground" />
                    ) : (
                      <span className="text-muted-foreground">{m.match_method ?? '—'}</span>
                    )}
                    {m.requires_review && !isManual && (
                      <Pill label="Review" className="ml-1 bg-status-yellow text-foreground" />
                    )}
                  </td>
                </tr>
              )
            })}
          </tbody>
        </table>
      </div>

      {unmatchedNeu.length > 0 && (
        <p className="text-xs text-muted-foreground">
          Ohne Zuordnung (neue Schritte in NEU): {unmatchedNeu.map((s) => label(s)).join(' · ')}
        </p>
      )}

      <div className="flex flex-wrap items-center gap-3 print:hidden">
        <Button onClick={apply} disabled={pending || !hasChanges} size="sm">
          <RotateCcw size={14} />
          {pending ? 'Berechne neu …' : 'Matching anwenden & neu berechnen'}
        </Button>
        {error && <span className="text-sm text-destructive">{error}</span>}
        <span className="text-xs text-muted-foreground">
          Manuelle Zuordnungen werden gespeichert (Methode „manuell") und alle Vergleichswerte neu berechnet.
        </span>
      </div>
    </div>
  )
}
