"use client"

import { Fragment, useMemo, useState } from "react"
import { useRouter } from "next/navigation"
import { ArrowUpRight, ChevronDown, ChevronRight, ChevronUp, ChevronsUpDown } from "lucide-react"

import type { HierarchyBenchmark, HierarchyComposite, HierarchyFamily } from "@/lib/backend-artifacts"
import { formatTagLabel } from "@/lib/benchmark-tags"
import type { BenchmarkCard } from "@/lib/benchmark-schema"
import type { BenchmarkEvalListItem } from "@/lib/eval-processing"
import { humanizeEvaluationId } from "@/lib/utils"

const LEAVES_INLINE_MAX = 50

export type FamilySortCol = "name" | "benchmarks" | "results"

interface FamilyTableProps {
  families: HierarchyFamily[]
  evalItems?: Map<string, BenchmarkEvalListItem>
  benchmarkCards?: Record<string, BenchmarkCard>
  domainFilter?: Set<string> | null
  categoryFilter?: Set<string> | null
  sortCol?: FamilySortCol
  sortDir?: "asc" | "desc"
  onSort?: (col: FamilySortCol) => void
}

function slugify(value: string | null | undefined): string {
  return (value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "")
}

const FAMILY_KEY_ACRONYMS = new Set([
  "llm", "llms", "aa", "hf", "api", "cli", "sql", "gpt", "qa", "ai", "ml",
  "nlp", "rl", "vqa", "vlm", "mt", "cv",
  // Security / safety / red-team families.
  "ctf", "cve", "gdm",
])
function humanizeFamilyKey(key: string): string {
  return key
    .split(/[_\-\s]+/)
    .filter(Boolean)
    .map((word) => {
      if (FAMILY_KEY_ACRONYMS.has(word.toLowerCase())) return word.toUpperCase()
      return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
    })
    .join("-")
}

interface LeafEntry {
  id: string
  leafKey: string
  leafName: string
  evalsCount: number
  domains: string[]
  tags: string[]
  description?: string | null
  sliceCount: number
}

/** A section inside the family accordion.
 *  "composite" — benchmarks that belong to a named composite group.
 *  "flat"      — standalone / direct benchmarks with no composite wrapper. */
interface AccordionSection {
  type: "composite" | "flat"
  key: string
  name: string
  description?: string | null
  leaves: LeafEntry[]
}

function buildLeafEntry(
  benchmark: HierarchyBenchmark,
  famKey: string,
  benchmarkCards?: Record<string, BenchmarkCard>,
): LeafEntry | null {
  const summaryIds = benchmark.summary_eval_ids ?? []
  const ids = summaryIds.length > 0
    ? summaryIds
    : benchmark.key ? [`${famKey}_${benchmark.key}`, benchmark.key] : []
  if (ids.length === 0) return null

  const collected = new Set<string>()
  for (const d of benchmark.tags?.domains ?? []) collected.add(d.toLowerCase())
  const cardByKey = benchmarkCards?.[benchmark.key]
  for (const d of cardByKey?.benchmark_details?.domains ?? []) collected.add(d.toLowerCase())
  for (const id of ids) {
    for (const d of benchmarkCards?.[id]?.benchmark_details?.domains ?? []) collected.add(d.toLowerCase())
  }

  const description =
    cardByKey?.benchmark_details?.overview ??
    benchmarkCards?.[ids[0]]?.benchmark_details?.overview ??
    null

  return {
    id: ids[0],
    leafKey: benchmark.key,
    leafName: benchmark.display_name || benchmark.key,
    evalsCount: ids.length,
    domains: Array.from(collected),
    tags: benchmark.derivedTags ?? [],
    description: description
      ? description.length > 120 ? description.slice(0, 117) + "…" : description
      : null,
    sliceCount: benchmark.slices?.length ?? 0,
  }
}

function collectFamilySections(
  fam: HierarchyFamily,
  benchmarkCards?: Record<string, BenchmarkCard>,
): AccordionSection[] {
  const sections: AccordionSection[] = []

  // 1. Composite groups first
  for (const composite of fam.composites ?? []) {
    const leaves: LeafEntry[] = []
    for (const bench of composite.benchmarks ?? []) {
      const entry = buildLeafEntry(bench, fam.key, benchmarkCards)
      if (entry) leaves.push(entry)
    }
    if (leaves.length === 0) continue
    const compDesc = benchmarkCards?.[composite.key]?.benchmark_details?.overview ?? null
    sections.push({
      type: "composite",
      key: composite.key,
      name: composite.display_name || composite.key,
      description: compDesc
        ? compDesc.length > 120 ? compDesc.slice(0, 117) + "…" : compDesc
        : null,
      leaves,
    })
  }

  // 2. Standalone + direct benchmarks — flat section (no header)
  const flatLeaves: LeafEntry[] = []
  for (const bench of [...(fam.standalone_benchmarks ?? []), ...(fam.benchmarks ?? [])]) {
    const entry = buildLeafEntry(bench, fam.key, benchmarkCards)
    if (entry) flatLeaves.push(entry)
  }
  if (flatLeaves.length > 0) {
    sections.push({ type: "flat", key: `${fam.key}--flat`, name: "", leaves: flatLeaves })
  }

  return sections
}

// Keep a flat list for filtering/counting
function collectLeafEntries(
  fam: HierarchyFamily,
  benchmarkCards?: Record<string, BenchmarkCard>,
): LeafEntry[] {
  const all: HierarchyBenchmark[] = [
    ...(fam.standalone_benchmarks ?? []),
    ...(fam.benchmarks ?? []),
    ...(fam.composites ?? []).flatMap((c: HierarchyComposite) => c.benchmarks ?? []),
  ]
  const out: LeafEntry[] = []
  for (const b of all) {
    const entry = buildLeafEntry(b, fam.key, benchmarkCards)
    if (entry) out.push(entry)
  }
  return out
}

function isFamilyDisplayNameMisleading(fam: HierarchyFamily, leafEntries: LeafEntry[]): boolean {
  const nameSlug = slugify(fam.display_name)
  if (!nameSlug) return false
  if (nameSlug === slugify(fam.key)) return false
  if (leafEntries.length < 2) return false
  return leafEntries.some(
    (l) => slugify(l.leafKey) === nameSlug || slugify(l.leafName) === nameSlug,
  )
}

export function getFamilyNavId(
  fam: HierarchyFamily,
  benchmarkCards?: Record<string, BenchmarkCard>,
): string | null {
  const leaves = collectLeafEntries(fam, benchmarkCards)
  if (leaves.length === 1) return leaves[0].id
  return null
}

interface RowData {
  key: string
  name: string
  keySlug: string
  tags: string[]
  benchmarks: number
  evalsCount: number
  leaves: LeafEntry[]
  sections: AccordionSection[]
  description: string | null
}

export function FamilyTable({
  families,
  evalItems,
  benchmarkCards,
  domainFilter,
  categoryFilter,
  sortCol,
  sortDir,
  onSort,
}: FamilyTableProps) {
  const router = useRouter()
  const [expanded, setExpanded] = useState<Record<string, boolean>>({})

  const domainFilterActive = Boolean(domainFilter && domainFilter.size > 0)
  const categoryFilterActive = Boolean(categoryFilter && categoryFilter.size > 0)
  const filterActive = domainFilterActive || categoryFilterActive

  function leafMatchesDomain(leaf: LeafEntry): boolean {
    if (!domainFilterActive || !domainFilter) return true
    return leaf.domains.some((d) => domainFilter.has(d))
  }

  function leafMatchesCategory(leaf: LeafEntry): boolean {
    if (!categoryFilterActive || !categoryFilter) return true
    return leaf.tags.some((t) => categoryFilter.has(t))
  }

  function leafMatchesFilter(leaf: LeafEntry): boolean {
    return leafMatchesDomain(leaf) && leafMatchesCategory(leaf)
  }

  function familyMatchesFilter(
    fam: HierarchyFamily,
    leafEntries: LeafEntry[],
  ): boolean {
    if (!filterActive) return true
    if (leafEntries.some(leafMatchesFilter)) return true
    if (categoryFilterActive && categoryFilter) {
      for (const tag of fam.derivedTags ?? []) {
        if (categoryFilter.has(tag) && !domainFilterActive) return true
      }
    }
    if (!domainFilterActive || !domainFilter) return false
    const sources: Array<string[]> = [
      benchmarkCards?.[fam.key]?.benchmark_details?.domains ?? [],
    ]
    for (const id of fam.eval_summary_ids ?? []) {
      sources.push(benchmarkCards?.[id]?.benchmark_details?.domains ?? [])
    }
    for (const list of sources) {
      for (const d of list) {
        if (domainFilter.has(d.trim().toLowerCase())) return true
      }
    }
    return false
  }

  const rows = useMemo<RowData[]>(() => {
    const out: RowData[] = []
    for (const fam of families) {
      const allBenchmarks = [
        ...(fam.standalone_benchmarks ?? []),
        ...(fam.benchmarks ?? []),
        ...(fam.composites ?? []).flatMap((c) => c.benchmarks ?? []),
      ]
      const metricCount = allBenchmarks.reduce((sum, b) => sum + (b.metrics?.length ?? 0), 0)
      const benchmarkCount = allBenchmarks.length

      const leafEntries = collectLeafEntries(fam, benchmarkCards)
      if (!familyMatchesFilter(fam, leafEntries)) continue

      const visibleLeafEntries = filterActive
        ? leafEntries.filter(leafMatchesFilter)
        : leafEntries

      const sections = collectFamilySections(fam, benchmarkCards).map((section) => ({
        ...section,
        leaves: filterActive ? section.leaves.filter(leafMatchesFilter) : section.leaves,
      })).filter((s) => s.leaves.length > 0)

      // Humanize when:
      //   - the upstream display_name is misleading (matches a leaf, not the family);
      //   - the display_name *is* the key (raw slug never humanized upstream); or
      //   - the display_name slugifies to the key (separators / casing differ but
      //     it's still the unhumanized form, e.g. "commonsense_qa" vs key
      //     "commonsense-qa").
      const slugMatchesKey =
        slugify(fam.display_name) === slugify(fam.key) && fam.display_name !== ""
      const displayName =
        isFamilyDisplayNameMisleading(fam, leafEntries) ||
        fam.display_name === fam.key ||
        slugMatchesKey
          ? humanizeFamilyKey(fam.key)
          : fam.display_name

      // Description: try family-level eval item (via a single benchmark family)
      // or fall back to the benchmark cards on the family's own leaves.
      let description: string | null = null
      if (evalItems && visibleLeafEntries.length === 1) {
        const overview = evalItems.get(visibleLeafEntries[0].id)?.benchmark_card?.benchmark_details?.overview
        if (overview) description = overview.length > 140 ? overview.slice(0, 137) + "…" : overview
      }

      // "Reported results" approximates the unique models tested in the
      // family. provenance_summary.total_results counts every (model,
      // metric) report row, so families that publish many metrics inflate
      // the number — agentharm's 4 declared metrics turned 18 unique
      // models into 72 result rows. Normalise by the family's metric
      // count (sum of bench-level metric entries; clamp to 1 to avoid
      // div-by-zero on families whose metrics array is empty).
      const totalResults =
        fam.provenance_summary?.total_results ??
        fam.reproducibility_summary?.results_total ??
        fam.evals_count ??
        metricCount
      const allBenchesForMetricCount: HierarchyBenchmark[] = [
        ...(fam.benchmarks ?? []),
        ...(fam.standalone_benchmarks ?? []),
        ...(fam.composites ?? []).flatMap((c) => c.benchmarks ?? []),
      ]
      const familyMetricCount = allBenchesForMetricCount.reduce(
        (n, b) => n + Math.max((b.metrics ?? []).length, 1),
        0,
      )
      const reportedResults =
        familyMetricCount > 0
          ? Math.round(totalResults / familyMetricCount)
          : totalResults
      out.push({
        key: fam.key,
        name: displayName,
        keySlug: fam.key,
        tags: fam.derivedTags ?? [],
        benchmarks: benchmarkCount,
        evalsCount: reportedResults,
        leaves: visibleLeafEntries,
        sections,
        description,
      })
    }
    return out
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [families, evalItems, benchmarkCards, domainFilter, categoryFilter])

  function SortIcon({ col }: { col: FamilySortCol }) {
    if (!onSort) return null
    if (sortCol !== col) return <ChevronsUpDown className="h-3 w-3 opacity-30" aria-hidden />
    return sortDir === "asc"
      ? <ChevronUp className="h-3 w-3" aria-hidden />
      : <ChevronDown className="h-3 w-3" aria-hidden />
  }

  function SortTh({
    col,
    children,
    className,
    style,
  }: {
    col: FamilySortCol
    children: React.ReactNode
    className?: string
    style?: React.CSSProperties
  }) {
    const active = sortCol === col
    return (
      <th
        className={className}
        style={{
          ...style,
          cursor: onSort ? "pointer" : undefined,
          userSelect: onSort ? "none" : undefined,
          color: active ? "var(--fg)" : undefined,
        }}
        onClick={onSort ? () => onSort(col) : undefined}
      >
        <span className="inline-flex items-center gap-1">
          {children}
          <SortIcon col={col} />
        </span>
      </th>
    )
  }

  return (
    <div className="overflow-x-auto">
      <table className="ec-htable">
        <thead>
          <tr>
            <SortTh col="name" style={{ width: "55%" }}>Family</SortTh>
            <th>Categories</th>
            <SortTh col="benchmarks" className="num">Benchmarks</SortTh>
            <SortTh col="results" className="num">Reported results</SortTh>
            <th style={{ width: 90 }} />
          </tr>
        </thead>
        <tbody>
          {rows.map((row) => {
            const isExpanded = filterActive
              ? expanded[row.key] ?? true
              : expanded[row.key] ?? false
            const allLeaves = row.sections.flatMap((s) => s.leaves)
            const hiddenLeafCount = isExpanded
              ? Math.max(allLeaves.length - LEAVES_INLINE_MAX, 0)
              : 0

            // Build visible sections respecting the per-section leaf cap
            let remaining = LEAVES_INLINE_MAX
            const visibleSections: AccordionSection[] = isExpanded
              ? row.sections.map((section) => {
                  const capped = section.leaves.slice(0, remaining)
                  remaining -= capped.length
                  return { ...section, leaves: capped }
                }).filter((s) => s.leaves.length > 0)
              : []

            return (
              <Fragment key={row.key}>
                <tr
                  onClick={() =>
                    setExpanded((current) => ({ ...current, [row.key]: !isExpanded }))
                  }
                  style={{ cursor: "pointer" }}
                >
                  <td>
                    <div className="flex items-start gap-2.5 min-w-0">
                      <button
                        type="button"
                        data-row-toggle
                        onClick={(e) => {
                          e.stopPropagation()
                          setExpanded((current) => ({ ...current, [row.key]: !isExpanded }))
                        }}
                        aria-expanded={isExpanded}
                        aria-label={isExpanded ? "Collapse family" : "Expand family"}
                        className="-ml-1 mt-0.5 inline-flex h-4 w-4 items-center justify-center transition-colors hover:text-[color:var(--accent)]"
                        style={{ color: "var(--fg-muted)" }}
                      >
                        {isExpanded ? <ChevronDown className="h-3 w-3" /> : <ChevronRight className="h-3 w-3" />}
                      </button>
                      <div className="min-w-0">
                        <div className="font-semibold text-[14px] text-[color:var(--fg)] truncate">
                          {row.name}
                        </div>
                        {row.description && (
                          <div
                            className="mt-0.5"
                            style={{ fontSize: 12, color: "var(--fg-muted)", lineHeight: 1.45, display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden" }}
                          >
                            {row.description}
                          </div>
                        )}
                        <div className="font-mono text-[10px] tracking-[0.06em] text-[color:var(--fg-subtle)] mt-0.5 truncate">
                          {row.keySlug}
                          <span className="ml-2 normal-case tracking-[0.04em]">
                            · {row.leaves.length} {row.leaves.length === 1 ? "benchmark" : "benchmarks"}
                          </span>
                        </div>
                      </div>
                    </div>
                  </td>
                  <td>
                    {row.tags.length === 0 ? (
                      <span className="inline-flex items-center font-mono text-[10px] uppercase tracking-[0.12em] text-[color:var(--fg-subtle)]">—</span>
                    ) : (
                      <div className="flex flex-wrap gap-1">
                        {row.tags.map((tag) => {
                          const highlighted = categoryFilter && categoryFilter.has(tag)
                          return (
                            <span
                              key={tag}
                              className="inline-flex items-center font-mono text-[10px] uppercase tracking-[0.12em] border px-1.5 py-0.5"
                              style={{
                                color: highlighted ? "var(--bg)" : "var(--fg-muted)",
                                borderColor: highlighted ? "var(--fg)" : "var(--border-soft)",
                                background: highlighted ? "var(--fg)" : "var(--bg)",
                              }}
                            >
                              {formatTagLabel(tag)}
                            </span>
                          )
                        })}
                      </div>
                    )}
                  </td>
                  <td className="num font-mono text-[13px]">{row.benchmarks.toLocaleString()}</td>
                  <td className="num font-mono text-[13px]">
                    {row.evalsCount.toLocaleString()}
                  </td>
                  <td>
                    <span className="font-mono text-[10px] tracking-[0.12em] uppercase text-[color:var(--accent)] inline-flex items-center gap-1">
                      {isExpanded ? "Hide" : "Browse"}
                    </span>
                  </td>
                </tr>

                {isExpanded && visibleSections.length > 0 && (
                  <tr style={{ background: "var(--bg-warm)" }}>
                    <td colSpan={5} style={{ padding: 0 }}>
                      <div style={{ padding: "10px 24px 14px 64px" }}>
                        <div
                          className="font-mono uppercase mb-2"
                          style={{ fontSize: 10, letterSpacing: "0.14em", color: "var(--fg-subtle)" }}
                        >
                          Benchmarks in this family · {row.leaves.length}
                        </div>

                        {visibleSections.map((section) => (
                          <div key={section.key} className="mb-3 last:mb-0">
                            {/* Composite header — not clickable to a page */}
                            {section.type === "composite" && (
                              <div
                                className="mb-1.5 flex items-baseline gap-2"
                                style={{ paddingBottom: 4, borderBottom: "1px solid var(--border-soft)" }}
                              >
                                <span
                                  className="font-semibold text-[12px]"
                                  style={{ color: "var(--fg-muted)" }}
                                >
                                  {section.name}
                                </span>
                                {section.description && (
                                  <span
                                    className="text-[11px] truncate"
                                    style={{ color: "var(--fg-subtle)" }}
                                  >
                                    {section.description}
                                  </span>
                                )}
                              </div>
                            )}

                            <ul
                              className="grid"
                              style={{
                                gridTemplateColumns: "repeat(auto-fill, minmax(260px, 1fr))",
                                gap: "0",
                                borderTop: "1px solid var(--border-soft)",
                                borderLeft: "1px solid var(--border-soft)",
                              }}
                            >
                              {section.leaves.map((leaf) => (
                                <li
                                  key={leaf.id}
                                  style={{
                                    borderBottom: "1px solid var(--border-soft)",
                                    borderRight: "1px solid var(--border-soft)",
                                  }}
                                >
                                  <button
                                    type="button"
                                    onClick={(e) => {
                                      e.stopPropagation()
                                      router.push(`/evals/${encodeURIComponent(leaf.id)}`)
                                    }}
                                    className="w-full flex items-start justify-between gap-2 px-3 py-2 text-left transition-colors hover:bg-[color:var(--bg-surface)]"
                                  >
                                    <div className="min-w-0 flex-1">
                                      <div className="flex items-center gap-1.5 flex-wrap">
                                        <span className="text-[13px] font-medium truncate text-[color:var(--fg)]">
                                          {leaf.leafName}
                                        </span>
                                        {leaf.sliceCount > 0 && (
                                          <span
                                            className="font-mono text-[9px] uppercase tracking-[0.1em] border px-1 py-px shrink-0"
                                            style={{ color: "var(--fg-subtle)", borderColor: "var(--border-soft)" }}
                                          >
                                            splits · {leaf.sliceCount}
                                          </span>
                                        )}
                                      </div>
                                      {leaf.description ? (
                                        <div
                                          className="font-mono truncate mt-0.5"
                                          style={{ fontSize: 10, color: "var(--fg-subtle)", letterSpacing: "0.03em" }}
                                        >
                                          {leaf.description}
                                        </div>
                                      ) : (
                                        <div
                                          className="font-mono truncate mt-0.5"
                                          style={{ fontSize: 10, color: "var(--fg-subtle)", letterSpacing: "0.04em" }}
                                        >
                                          {humanizeEvaluationId(leaf.id)}
                                        </div>
                                      )}
                                    </div>
                                    <ArrowUpRight
                                      className="h-3 w-3 shrink-0 mt-0.5"
                                      style={{ color: "var(--accent)" }}
                                      aria-hidden
                                    />
                                  </button>
                                </li>
                              ))}
                            </ul>
                          </div>
                        ))}

                        {hiddenLeafCount > 0 && (
                          <div
                            className="mt-2 font-mono uppercase"
                            style={{ fontSize: 10, letterSpacing: "0.14em", color: "var(--fg-subtle)" }}
                          >
                            {hiddenLeafCount} more benchmarks not shown
                          </div>
                        )}
                      </div>
                    </td>
                  </tr>
                )}
              </Fragment>
            )
          })}
        </tbody>
      </table>
    </div>
  )
}
