'use client'

// tdd-guard:skip — client UI: upload form + result/recent-comparison rendering (KAR-799)

import { useRef, useState, useTransition } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { Upload, AlertTriangle, CheckCircle2, ChevronRight, GripVertical, X, ArrowUp, ArrowDown, Info } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { uploadWithProgress } from '@/components/qaf-differences/qaf-upload-constants'
import { QafVariantVsStandardCreate } from '@/components/qaf-differences/qaf-variant-vs-standard-create'
import { QafSupplierBenchmarkCreate } from '@/components/qaf-differences/qaf-supplier-benchmark-create'
import {
  analyzeQafBatchFromStorage,
  createQafUploadTargets,
  discardQafUploads,
  type BatchAnalysisSummary,
} from '@/app/qaf-differences/actions'

interface ProjectOption {
  id: string
  project_code: string | null
  supplier_name: string | null
}

interface ComparisonRow {
  id: string
  part_number: string | null
  title?: string | null
  tags?: string[] | null
  status: string | null
  baseline_status: string | null
  created_at: string | null
  comparison_mode?: string | null
  // PostgREST embed: single object at runtime, array in the inferred type.
  projects?: { project_code: string | null; supplier_name: string | null } | { project_code: string | null; supplier_name: string | null }[] | null
}

const dateFmt = new Intl.DateTimeFormat('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })

const STATUS_EXPLAIN: Record<string, string> = {
  draft: 'Analyse erstellt, noch nicht bestätigt/abgeschlossen.',
  reviewed: 'Vom Nutzer als geprüft markiert.',
  // G60/Summary reason only — see BASELINE_REVIEW_EXPLAIN_MULTI_QAF below for
  // the distinct Multi-QAF reason (KAR-942 adversarial-review F6 fix).
  baseline_review:
    'Bitte prüfen, ob ALT/NEU richtig gewählt ist — bei G60 gilt die Upload-Reihenfolge, bei Summary fehlten Angebotsdaten.',
  insufficient: 'Baseline unzureichend — die Dateien liefern zu wenig Daten für eine verlässliche ALT/NEU-Wahl.',
}

// KAR-942 adversarial-review F6 fix (13.07.2026): this PR sets
// qaf_comparison.baseline_status='baseline_review' for multi_qaf comparisons
// too (whenever runMultiQafCompareFlow's reviewRequired is true), but the
// shared badge below used to always pull STATUS_EXPLAIN.baseline_review — a
// G60/Summary-only text talking about ALT/NEU upload order or missing
// quotation dates, neither of which is the real Multi-QAF reason (uncertain
// variant matches / structural drift / dropped VariantMatchOverrides).
const BASELINE_REVIEW_EXPLAIN_MULTI_QAF =
  'Bitte prüfen — unsichere Varianten-Zuordnung, strukturelle Abweichung oder eine verworfene manuelle Zuordnung. / Please review — uncertain variant matching, structural drift, or a dropped manual match.'

// KAR-948: the third baseline_status-setting mode (variant-vs-standard,
// runVariantVsStandardCompare) has its OWN reasons — a container-level
// warning, this specific variant's material data below the KAR-936
// aggregation gate, or a critical summary-identity finding (variant-vs-
// standard.ts's own reviewRequiredReasons) — none of which are the Multi-QAF
// container-matching reason above, so this mode gets its own text rather
// than silently reusing either sibling's (same "don't show a misleading
// reason" discipline the KAR-942 F6 fix above already established).
const BASELINE_REVIEW_EXPLAIN_VARIANT_VS_STANDARD =
  'Bitte prüfen — Container-Warnung, unsichere Material-Zuordnung dieser Variante oder ein kritischer Summary-Identitäts-Befund. / Please review — a container warning, uncertain material assignment for this variant, or a critical summary-identity finding.'

/** Mode-aware tooltip for a comparison's baseline_status badge — only
 * 'baseline_review' has a mode-dependent reason today (see comment above);
 * every other status/mode combination falls straight through to the shared
 * STATUS_EXPLAIN text, unchanged. */
function explainBaselineStatus(status: string, mode: string | null | undefined): string | undefined {
  if (status === 'baseline_review' && mode === 'multi_qaf') return BASELINE_REVIEW_EXPLAIN_MULTI_QAF
  if (status === 'baseline_review' && mode === 'multi_qaf_variant_vs_standard') return BASELINE_REVIEW_EXPLAIN_VARIANT_VS_STANDARD
  return STATUS_EXPLAIN[status]
}

const BASELINE_BADGE: Record<string, string> = {
  baseline_review: 'Baseline-Review',
  insufficient: 'Baseline unzureichend',
}

interface Props {
  projects: ProjectOption[]
  recentComparisons: ComparisonRow[]
}

export function QafDifferencesClient({ projects, recentComparisons }: Props) {
  const router = useRouter()
  const [projectId, setProjectId] = useState(projects[0]?.id ?? '')
  const [pending, startTransition] = useTransition()
  const [result, setResult] = useState<BatchAnalysisSummary | null>(null)
  const [error, setError] = useState<string | null>(null)
  const [progress, setProgress] = useState<string | null>(null)
  // List search/filter (KAR-848).
  const [listQuery, setListQuery] = useState('')
  const [statusFilter, setStatusFilter] = useState<string | null>(null)
  const [tagFilter, setTagFilter] = useState<string | null>(null)
  // Upload order IS the comparison order (top = ALT) — user-sortable per DnD.
  const [orderedFiles, setOrderedFiles] = useState<File[]>([])
  const [uploadPct, setUploadPct] = useState<number | null>(null)
  const dragIndex = useRef<number | null>(null)
  const filteredComparisons = recentComparisons.filter((c) => {
    const q = listQuery.trim().toLowerCase()
    const proj = Array.isArray(c.projects) ? c.projects[0] : c.projects
    const hay = `${c.title ?? ''} ${c.part_number ?? ''} ${proj?.project_code ?? ''} ${proj?.supplier_name ?? ''}`.toLowerCase()
    if (q && !hay.includes(q)) return false
    if (statusFilter && c.status !== statusFilter) return false
    if (tagFilter && !(c.tags ?? []).includes(tagFilter)) return false
    return true
  })
  const fileRef = useRef<HTMLInputElement>(null)

  function moveFile(from: number, to: number) {
    setOrderedFiles((list) => {
      if (to < 0 || to >= list.length) return list
      const next = [...list]
      const [moved] = next.splice(from, 1)
      next.splice(to, 0, moved)
      return next
    })
  }

  function removeFile(index: number) {
    setOrderedFiles((list) => list.filter((_, i) => i !== index))
  }

  function onAnalyze() {
    setError(null)
    setResult(null)
    const list = orderedFiles
    if (!projectId) return setError('Bitte ein Projekt wählen.')
    if (list.length === 0) return setError('Bitte mindestens eine QAF-Datei wählen.')
    startTransition(async () => {
      try {
        // 1) Signed upload targets (server validates ownership + files).
        setProgress('Upload wird vorbereitet …')
        setUploadPct(0)
        const targetsRes = await createQafUploadTargets(
          projectId,
          list.map((f) => ({ name: f.name, size: f.size })),
        )
        if (!targetsRes.ok) return setError(targetsRes.error)

        // 2) Browser → Supabase Storage directly via XHR (byte progress).
        // Parallel; on any failure best-effort discard so nothing orphans.
        const totalBytes = list.reduce((s, f) => s + f.size, 0)
        const loadedPerFile = list.map(() => 0)
        let lastPct = -1
        const report = () => {
          const pct = totalBytes > 0 ? Math.min(100, (loadedPerFile.reduce((s, v) => s + v, 0) / totalBytes) * 100) : 100
          // onprogress fires very often — only re-render on full-percent steps.
          if (Math.floor(pct) > lastPct) {
            lastPct = Math.floor(pct)
            setUploadPct(pct)
          }
        }
        setProgress(`Lade ${list.length} Datei(en) hoch …`)
        const uploadResults = await Promise.all(
          list.map((file, i) =>
            uploadWithProgress(targetsRes.data[i].signedUrl, file, (bytes) => {
              loadedPerFile[i] = bytes
              report()
            })
              .then(() => ({ name: file.name, err: null as string | null }))
              .catch((e: Error) => ({ name: file.name, err: e.message })),
          ),
        )
        const failedUpload = uploadResults.find((r) => r.err)
        if (failedUpload) {
          void discardQafUploads(projectId, targetsRes.data.map((t) => t.path))
          return setError(`${failedUpload.name}: Upload fehlgeschlagen — ${failedUpload.err}`)
        }

        // 3) Server-side parse + analysis from storage.
        setUploadPct(null)
        setProgress('Analysiere …')
        const res = await analyzeQafBatchFromStorage(
          projectId,
          targetsRes.data.map((t) => ({ path: t.path, name: t.name })),
        )
        if (res.ok) {
          setResult(res.data)
          setOrderedFiles([])
          if (fileRef.current) fileRef.current.value = ''
          // Re-fetch the server-rendered "Letzte Vergleiche" list so the new
          // comparison appears without a manual reload.
          router.refresh()
        } else {
          setError(res.error)
        }
      } finally {
        setProgress(null)
        setUploadPct(null)
      }
    })
  }

  return (
    <div className="space-y-6">
      {/* Upload */}
      <section className="rounded border border-border bg-card p-4 space-y-4">
        <h2 className="font-display uppercase tracking-tight text-sm font-bold text-foreground">Upload</h2>
        <details className="rounded border border-border/50 bg-muted/20 px-3 py-1.5 text-xs text-muted-foreground">
          <summary className="cursor-pointer select-none font-medium">ⓘ Wie funktioniert die Analyse?</summary>
          <div className="space-y-1 pt-1.5">
            <p>
              QAF-Dateien (Quotation Analysis Form, .xlsx/.xlsm/.xls) hochladen — die App erkennt den Typ automatisch:
              Summary-QAFs werden nach Sachnummer gruppiert und paarweise verglichen (ALT = älteres Angebotsdatum);
              zwei G60-Detail-QAFs werden als Ganzes verglichen (Reihenfolge oben = ALT). Jede Analyse zeigt dieselben
              nummerierten Sektionen 1–14.
            </p>
            <p>Beispiel: Basis-QAF von 2022 + Repricing von 2026 hochladen → Vergleich mit Kostenbrücke, Preistreibern, Hebeln und Hochrechnung.</p>
          </div>
        </details>

        <div className="grid gap-4 sm:grid-cols-2">
          <label className="space-y-1 text-sm">
            <span className="text-muted-foreground">Projekt</span>
            <select
              value={projectId}
              onChange={(e) => setProjectId(e.target.value)}
              className="w-full rounded border border-border bg-background px-3 py-2 text-sm text-foreground"
            >
              {projects.length === 0 && <option value="">Keine Projekte</option>}
              {projects.map((p) => (
                <option key={p.id} value={p.id}>
                  {[p.project_code, p.supplier_name].filter(Boolean).join(' — ') || p.id}
                </option>
              ))}
            </select>
          </label>

          <label className="space-y-1 text-sm">
            <span className="text-muted-foreground">QAF-Dateien (.xlsx, .xlsm, .xls)</span>
            <input
              ref={fileRef}
              type="file"
              multiple
              accept=".xlsx,.xlsm,.xls,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel.sheet.macroEnabled.12,application/vnd.ms-excel"
              disabled={pending}
              onChange={(e) => {
                const picked = Array.from(e.target.files ?? [])
                // Append (dedup by name+size) — re-opening the picker must not
                // wipe an already arranged order (review #255).
                setOrderedFiles((prev) => {
                  const seen = new Set(prev.map((f) => `${f.name}|${f.size}`))
                  return [...prev, ...picked.filter((f) => !seen.has(`${f.name}|${f.size}`))]
                })
                // The list below is the single source of truth — reset the
                // input so its native "N files" label can't disagree.
                e.target.value = ''
              }}
              className="w-full rounded border border-border bg-background px-3 py-2 text-sm text-foreground file:mr-3 file:rounded file:border-0 file:bg-muted file:px-3 file:py-1 file:text-sm"
            />
          </label>
        </div>

        {orderedFiles.length > 0 && (
          <div className="space-y-1">
            <p className="text-xs text-muted-foreground">
              Reihenfolge per Drag&nbsp;&amp;&nbsp;Drop oder Pfeilen: oben = ALT/Basis. Bei G60-Detail-QAFs gilt sie
              direkt; bei Summary-QAFs entscheidet das Angebotsdatum in der Datei — die Reihenfolge greift, wenn
              Daten fehlen oder gleich sind.
            </p>
            <ul className="space-y-1">
              {orderedFiles.map((f, i) => (
                <li
                  key={`${f.name}|${f.size}|${f.lastModified}`}
                  draggable={!pending}
                  onDragStart={() => {
                    dragIndex.current = i
                  }}
                  onDragOver={(e) => e.preventDefault()}
                  onDrop={(e) => {
                    e.preventDefault()
                    if (dragIndex.current !== null && dragIndex.current !== i) moveFile(dragIndex.current, i)
                    dragIndex.current = null
                  }}
                  className="flex items-center gap-2 rounded border border-border bg-background px-2 py-1.5 text-sm"
                >
                  <GripVertical size={14} className="shrink-0 cursor-grab text-muted-foreground" />
                  <span className="w-14 shrink-0 rounded bg-muted px-1.5 py-0.5 text-center text-xs font-medium text-muted-foreground">
                    {i === 0 ? 'ALT' : i === 1 && orderedFiles.length === 2 ? 'NEU' : `#${i + 1}`}
                  </span>
                  <span className="min-w-0 flex-1 truncate text-foreground">{f.name}</span>
                  <span className="shrink-0 text-xs text-muted-foreground">{(f.size / 1024 / 1024).toFixed(1)} MB</span>
                  <button
                    type="button"
                    onClick={() => moveFile(i, i - 1)}
                    disabled={i === 0}
                    aria-label="nach oben"
                    className="text-muted-foreground hover:text-foreground disabled:opacity-30"
                  >
                    <ArrowUp size={14} />
                  </button>
                  <button
                    type="button"
                    onClick={() => moveFile(i, i + 1)}
                    disabled={i === orderedFiles.length - 1}
                    aria-label="nach unten"
                    className="text-muted-foreground hover:text-foreground disabled:opacity-30"
                  >
                    <ArrowDown size={14} />
                  </button>
                  <button
                    type="button"
                    onClick={() => removeFile(i)}
                    aria-label="entfernen"
                    className="text-muted-foreground hover:text-destructive"
                  >
                    <X size={14} />
                  </button>
                </li>
              ))}
            </ul>
          </div>
        )}

        <div className="flex items-center gap-3">
          <Button onClick={onAnalyze} disabled={pending} size="lg">
            <Upload size={16} />
            {pending ? 'Läuft…' : 'Analysieren'}
          </Button>
          {pending && progress && (
            <span className="flex min-w-0 flex-1 items-center gap-2 text-sm text-muted-foreground">
              <span className="shrink-0">{progress}</span>
              <span className="h-2 w-full max-w-56 overflow-hidden rounded bg-muted">
                <span
                  className={`block h-full rounded bg-primary transition-[width] duration-200 ${uploadPct === null ? 'w-1/3 animate-pulse' : ''}`}
                  style={uploadPct !== null ? { width: `${uploadPct}%` } : undefined}
                />
              </span>
              {uploadPct !== null && <span className="shrink-0 tabular-nums">{Math.round(uploadPct)} %</span>}
            </span>
          )}
          {error && (
            <span className="flex items-center gap-1 text-sm text-destructive">
              <AlertTriangle size={14} />
              {error}
            </span>
          )}
        </div>
      </section>

      {/* Variante ↔ Standard-QAF (KAR-948, Master-Prompt §15 Szenario B) —
          additive secondary flow, collapsed by default: standard-QAF users
          never see anything change above. */}
      {/* key={projectId}: a project change fully remounts the fetched-list
          state instead of needing a second effect/handler to detect it —
          see qaf-variant-vs-standard-create.tsx's own loadLists() doc. */}
      <QafVariantVsStandardCreate key={projectId} projectId={projectId} />
      {/* KAR-993: Lieferanten-Benchmark — zwei Angebote VERSCHIEDENER Lieferanten,
          matching-frei. Eigene Flaeche, weil die Auswahl eine andere ist (zwei
          Dateien, keine Varianten-Wahl). */}
      <QafSupplierBenchmarkCreate key={`bench-${projectId}`} projectId={projectId} />

      {/* Result */}
      {result && (
        <section className="rounded border border-border bg-card p-4 space-y-3">
          <h2 className="flex items-center gap-2 font-display uppercase tracking-tight text-sm font-bold text-foreground">
            <CheckCircle2 size={16} className="text-primary" />
            Ergebnis
          </h2>
          <div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
            <Stat label="Vergleiche" value={result.comparisons} />
            <Stat label="Dateien geparst" value={result.filesParsed} />
            <Stat label="Review nötig" value={result.groupsNeedingReview} />
            <Stat label="Datei-Fehler" value={result.fileErrors.length} />
          </div>
          {result.fileErrors.length > 0 && (
            <ul className="space-y-1 text-sm text-muted-foreground">
              {result.fileErrors.map((e, i) => (
                <li key={i} className="flex items-center gap-1">
                  <AlertTriangle size={14} className="text-destructive" />
                  <span className="font-medium text-foreground">{e.file}</span>: {e.error}
                </li>
              ))}
            </ul>
          )}
          {/* KAR-935 adversarial-review F3 fix: a Multi-QAF detection is a
              SUCCESS outcome (file recognized + saved), not a file error —
              rendered via its own neutral/informational list, never with the
              destructive AlertTriangle styling `fileErrors` above uses. */}
          {result.multiQafNotices.length > 0 && (
            <ul className="space-y-1 text-sm text-muted-foreground">
              {result.multiQafNotices.map((n, i) => (
                <li key={i} className="flex items-center gap-1">
                  <Info size={14} className="text-primary" />
                  <span className="font-medium text-foreground">{n.file}</span>: {n.message}
                </li>
              ))}
            </ul>
          )}
        </section>
      )}

      {/* Recent comparisons */}
      <section className="rounded border border-border bg-card p-4 space-y-3">
        <h2 className="font-display uppercase tracking-tight text-sm font-bold text-foreground">Letzte Vergleiche</h2>
        <details className="rounded border border-border/50 bg-muted/20 px-3 py-1.5 text-xs text-muted-foreground">
          <summary className="cursor-pointer select-none font-medium">ⓘ Was bedeuten die Status?</summary>
          <div className="space-y-1 pt-1.5">
            <p>
              <span className="font-medium text-foreground">draft</span> — {STATUS_EXPLAIN.draft}
            </p>
            <p>
              <span className="font-medium text-foreground">Baseline-Review</span> — {STATUS_EXPLAIN.baseline_review}
            </p>
            <p>
              <span className="font-medium text-foreground">Baseline-Review (Multi-QAF)</span> —{' '}
              {BASELINE_REVIEW_EXPLAIN_MULTI_QAF}
            </p>
            <p>
              <span className="font-medium text-foreground">Baseline-Review (Variante ↔ Standard)</span> —{' '}
              {BASELINE_REVIEW_EXPLAIN_VARIANT_VS_STANDARD}
            </p>
            <p>
              <span className="font-medium text-foreground">Baseline unzureichend</span> —{' '}
              {STATUS_EXPLAIN.insufficient}
            </p>
            <p>
              <span className="font-medium text-foreground">G60</span> — Detail-QAF-Vergleich über alle Kostenreiter
              (statt Summary-Blatt).
            </p>
          </div>
        </details>
        <div className="flex flex-wrap items-center gap-2">
          <input
            type="search"
            value={listQuery}
            onChange={(e) => setListQuery(e.target.value)}
            placeholder="Suchen (Name, Sachnummer, Projekt) …"
            className="w-64 rounded border border-border bg-background px-2 py-1.5 text-sm text-foreground"
          />
          {['draft', 'reviewed'].map((st) => (
            <button
              key={st}
              type="button"
              onClick={() => setStatusFilter(statusFilter === st ? null : st)}
              className={`rounded border px-2 py-1 text-xs ${
                statusFilter === st
                  ? 'border-primary bg-primary/10 text-foreground'
                  : 'border-border text-muted-foreground hover:text-foreground'
              }`}
            >
              {st === 'draft' ? 'Entwurf' : 'geprüft'}
            </button>
          ))}
          {[...new Set(recentComparisons.flatMap((c) => c.tags ?? []))].map((t) => (
            <button
              key={t}
              type="button"
              onClick={() => setTagFilter(tagFilter === t ? null : t)}
              className={`rounded border px-2 py-1 text-xs ${
                tagFilter === t
                  ? 'border-primary bg-primary/10 text-foreground'
                  : 'border-border text-muted-foreground hover:text-foreground'
              }`}
            >
              {t}
            </button>
          ))}
        </div>
        {filteredComparisons.length === 0 ? (
          <p className="text-sm text-muted-foreground">
            {recentComparisons.length === 0 ? 'Noch keine Vergleiche.' : 'Keine Treffer.'}
          </p>
        ) : (
          <ul className="divide-y divide-border">
            {filteredComparisons.map((c) => (
              <li key={c.id}>
                <Link
                  href={`/qaf-differences/${c.id}`}
                  className="flex items-center justify-between gap-2 py-2 text-sm hover:bg-muted/50 -mx-2 px-2 rounded"
                >
                  <span className="flex min-w-0 flex-col">
                    <span className="truncate font-medium text-foreground">
                      {c.title ||
                        c.part_number ||
                        (c.comparison_mode === 'g60'
                          ? 'G60-Detailvergleich'
                          : c.comparison_mode === 'multi_qaf'
                            ? 'Multi-QAF-Vergleich'
                            : c.comparison_mode === 'multi_qaf_variant_vs_standard'
                              ? 'Variante ↔ Standard-Vergleich'
                              : 'Vergleich (Sachnummer fehlt)')}
                    </span>
                    <span className="truncate text-xs text-muted-foreground">
                      {(() => {
                        const p = Array.isArray(c.projects) ? c.projects[0] : c.projects
                        return [p?.project_code, p?.supplier_name].filter(Boolean).join(' — ') || '—'
                      })()}
                      {c.created_at ? ` · ${dateFmt.format(new Date(c.created_at))}` : ''}
                    </span>
                  </span>
                  <span className="flex shrink-0 items-center gap-2">
                    {(c.tags ?? []).map((t) => (
                      <Badge key={t} variant="outline">
                        {t}
                      </Badge>
                    ))}
                    {c.comparison_mode === 'g60' && !(c.tags ?? []).includes('G60') && <Badge variant="outline">G60</Badge>}
                    {c.comparison_mode === 'multi_qaf' && <Badge variant="outline">Multi-QAF</Badge>}
                    {c.comparison_mode === 'multi_qaf_variant_vs_standard' && <Badge variant="outline">Variante↔Standard</Badge>}
                    {c.baseline_status && c.baseline_status !== 'ok' && (
                      <Badge variant="outline" title={explainBaselineStatus(c.baseline_status, c.comparison_mode)}>
                        {BASELINE_BADGE[c.baseline_status] ?? c.baseline_status}
                      </Badge>
                    )}
                    <span className="text-muted-foreground" title={c.status ? STATUS_EXPLAIN[c.status] : undefined}>
                      {c.status ?? ''}
                    </span>
                    <ChevronRight size={16} className="text-muted-foreground" />
                  </span>
                </Link>
              </li>
            ))}
          </ul>
        )}
      </section>
    </div>
  )
}

function Stat({ label, value }: { label: string; value: number }) {
  return (
    <div className="rounded border border-border bg-background p-3">
      <div className="text-2xl font-bold text-foreground">{value}</div>
      <div className="text-xs text-muted-foreground">{label}</div>
    </div>
  )
}
