"use client"

import { useCallback, useDeferredValue, useEffect, useMemo, useState } from "react"
import { ArrowRightLeft, Search, X } from "lucide-react"

import { type BenchmarkEvaluationCardData } from "@/components/benchmark-evaluation-card"
import { DeveloperTable } from "@/components/developer-table"
import { InfiniteScrollSentinel } from "@/components/infinite-scroll"
import { ModelCompareDialog } from "@/components/model-compare-dialog"
import { ModelTable } from "@/components/model-table"
import { Navigation } from "@/components/navigation"
import { ParamRangePicker } from "@/components/param-range-picker"
import { fetchDevelopers, fetchModelCards, fetchBenchmarkMetadata, type DeveloperListItem } from "@/lib/dashboard-data-client"
import type { BenchmarkCard } from "@/lib/benchmark-schema"
import { PARAM_RANGE_MAX_INDEX, paramStepToNumeric } from "@/lib/param-range"

const PAGE_SIZE = 40
const MAX_COMPARE_MODELS = 4

type ModelSort = "benchmarks" | "results" | "name" | "released" | "params"
type DevSort = "coverage" | "evaluated" | "models" | "name"

function safeTimestamp(value: string | null | undefined) {
  if (!value) return 0
  const numeric = Number(value)
  if (!Number.isNaN(numeric) && !value.includes("-")) {
    return numeric > 1_000_000_000_000 ? numeric : numeric * 1000
  }
  const parsed = new Date(value).getTime()
  return Number.isNaN(parsed) ? 0 : parsed
}

export default function ModelsPage() {
  const [evaluations, setEvaluations] = useState<BenchmarkEvaluationCardData[]>([])
  const [developers, setDevelopers] = useState<DeveloperListItem[]>([])
  const [benchmarkCards, setBenchmarkCards] = useState<Record<string, BenchmarkCard>>({})
  const [loadingModels, setLoadingModels] = useState(true)
  const [loadingDevelopers, setLoadingDevelopers] = useState(false)
  const [developersReady, setDevelopersReady] = useState(false)
  const [groupByDeveloper, setGroupByDeveloper] = useState(false)
  const [modelSortBy, setModelSortBy] = useState<ModelSort>("benchmarks")
  const [developerSortBy, setDeveloperSortBy] = useState<DevSort>("coverage")
  const [searchQuery, setSearchQuery] = useState("")
  const [selectedModelIds, setSelectedModelIds] = useState<string[]>([])
  const [compareOpen, setCompareOpen] = useState(false)
  const [visibleCount, setVisibleCount] = useState(PAGE_SIZE)
  const [minParamStep, setMinParamStep] = useState(0)
  const [maxParamStep, setMaxParamStep] = useState(PARAM_RANGE_MAX_INDEX)
  const [showUnknownSize, setShowUnknownSize] = useState(true)
  const deferredSearchQuery = useDeferredValue(searchQuery)
  const numericMinParams = useMemo(() => paramStepToNumeric(minParamStep, "min"), [minParamStep])
  const numericMaxParams = useMemo(() => paramStepToNumeric(maxParamStep, "max"), [maxParamStep])

  useEffect(() => {
    Promise.all([fetchModelCards(), fetchBenchmarkMetadata()])
      .then(([cards, metadata]) => {
        setEvaluations(cards)
        setBenchmarkCards(metadata)
      })
      .catch((error) => {
        console.error("Failed to load evaluations:", error)
      })
      .finally(() => setLoadingModels(false))
  }, [])

  useEffect(() => {
    if (!groupByDeveloper || developersReady || loadingDevelopers) return
    setLoadingDevelopers(true)
    fetchDevelopers()
      .then(setDevelopers)
      .catch((error) => {
        console.error("Failed to load developers:", error)
      })
      .finally(() => {
        setLoadingDevelopers(false)
        setDevelopersReady(true)
      })
  }, [developersReady, groupByDeveloper, loadingDevelopers])

  const totalBenchmarks = useMemo(() => Object.keys(benchmarkCards).length, [benchmarkCards])

  // Models — filter + sort
  const sortedEvaluations = useMemo(() => {
    const query = deferredSearchQuery.trim().toLowerCase()
    let filtered = evaluations

    filtered = filtered.filter((row) => {
      if (row.params_billions == null) return showUnknownSize
      if (numericMinParams != null && row.params_billions < numericMinParams) return false
      if (numericMaxParams != null && row.params_billions > numericMaxParams) return false
      return true
    })

    if (query) {
      filtered = filtered.filter((row) => {
        return (
          row.model_name.toLowerCase().includes(query) ||
          row.canonical_model_name.toLowerCase().includes(query) ||
          row.developer.toLowerCase().includes(query) ||
          (row.benchmark_names ?? []).some((b) => b.toLowerCase().includes(query))
        )
      })
    }

    return filtered.slice().sort((a, b) => {
      switch (modelSortBy) {
        case "name":
          return a.model_name.localeCompare(b.model_name)
        case "released":
          return safeTimestamp(b.release_date ?? b.latest_timestamp) - safeTimestamp(a.release_date ?? a.latest_timestamp)
        case "params":
          return (b.params_billions ?? 0) - (a.params_billions ?? 0)
        case "results":
          return b.evaluations_count - a.evaluations_count
        case "benchmarks":
        default:
          return b.benchmarks_count - a.benchmarks_count
      }
    })
  }, [evaluations, deferredSearchQuery, modelSortBy, numericMinParams, numericMaxParams, showUnknownSize])

  // Developers — filter + sort
  const sortedDevelopers = useMemo(() => {
    const query = deferredSearchQuery.trim().toLowerCase()
    let filtered = developers

    if (query) {
      filtered = filtered.filter(
        (dev) =>
          dev.developer.toLowerCase().includes(query) ||
          dev.popular_evals.some((ev) => ev.benchmark.toLowerCase().includes(query)),
      )
    }

    return filtered.slice().sort((a, b) => {
      switch (developerSortBy) {
        case "evaluated":
          return b.evaluation_count - a.evaluation_count
        case "models":
          return b.model_count - a.model_count
        case "name":
          return a.developer.localeCompare(b.developer)
        case "coverage":
        default:
          return b.benchmark_count - a.benchmark_count
      }
    })
  }, [developers, deferredSearchQuery, developerSortBy])

  // Reset visible window when filter/sort changes
  useEffect(() => {
    setVisibleCount(PAGE_SIZE)
  }, [groupByDeveloper, modelSortBy, developerSortBy, deferredSearchQuery, minParamStep, maxParamStep, showUnknownSize])

  const totalCount = groupByDeveloper ? sortedDevelopers.length : sortedEvaluations.length
  const visibleEvaluations = useMemo(
    () => sortedEvaluations.slice(0, visibleCount),
    [sortedEvaluations, visibleCount],
  )
  const visibleDevelopers = useMemo(
    () => sortedDevelopers.slice(0, visibleCount),
    [sortedDevelopers, visibleCount],
  )
  const hasMore = visibleCount < totalCount
  const handleLoadMore = useCallback(() => {
    setVisibleCount((current) => Math.min(current + PAGE_SIZE, totalCount))
  }, [totalCount])

  const toggleModelSelection = useCallback((id: string) => {
    setSelectedModelIds((current) => {
      if (current.includes(id)) return current.filter((existing) => existing !== id)
      if (current.length >= MAX_COMPARE_MODELS) return current
      return [...current, id]
    })
  }, [])

  const selectedModels = useMemo(
    () =>
      selectedModelIds
        .map((id) => evaluations.find((evaluation) => evaluation.id === id))
        .filter((evaluation): evaluation is BenchmarkEvaluationCardData => Boolean(evaluation)),
    [evaluations, selectedModelIds],
  )

  const loading = loadingModels || (groupByDeveloper && !developersReady)
  const totalResults = useMemo(
    () => sortedEvaluations.reduce((sum, row) => sum + row.evaluations_count, 0),
    [sortedEvaluations],
  )

  return (
    <div className="min-h-screen bg-background">
      <Navigation />

      <main className="mx-auto w-full max-w-[96rem] px-4 pt-12 pb-24 sm:px-8">
        {/* HEADER --------------------------------------------------- */}
        <div className="kicker">Index</div>
        <h1 className="ec-page-h1">{groupByDeveloper ? "Model developers" : "Models"}</h1>
        <p className="ec-page-lede">
          {groupByDeveloper
            ? "Every reporting organisation in the corpus and the breadth of evaluation it ships."
            : (
              <>
                Every indexed model and the shape of its published evaluation record. Coverage shows the
                share of the{" "}
                <span className="font-mono text-[13px]">
                  {totalBenchmarks.toLocaleString()}-benchmark
                </span>{" "}
                registry that the developer (or a third party) has reported on.
              </>
            )}
        </p>

        {/* META + FILTER BAR — single row -------------------------- */}
        <div className="mb-6 flex flex-wrap items-center gap-x-8 gap-y-3 border-y border-[color:var(--border-soft)] py-4">
          {/* Inline meta — mono kicker stat strip */}
          <div className="flex shrink-0 flex-wrap items-baseline gap-x-4 gap-y-1 font-mono text-[11px] tracking-[0.1em] uppercase text-[color:var(--fg-subtle)]">
            <span>
              <span className="text-[color:var(--fg)] tabular-nums font-semibold mr-1">
                {totalCount.toLocaleString()}
              </span>
              {groupByDeveloper ? "developers" : "models"}
            </span>
            {!groupByDeveloper && (
              <span>
                · <span className="text-[color:var(--fg)] tabular-nums font-semibold mr-1">{totalResults.toLocaleString()}</span>
                results
              </span>
            )}
            {!groupByDeveloper && (
              <span>
                · <span className="text-[color:var(--fg)] tabular-nums font-semibold mr-1">{selectedModels.length}/{MAX_COMPARE_MODELS}</span>
                tray
              </span>
            )}
          </div>

          <span className="hidden h-5 w-px bg-[color:var(--border-soft)] sm:block" />

          <div className="ec-mode-toggle">
            <button
              type="button"
              className={!groupByDeveloper ? "on" : ""}
              onClick={() => setGroupByDeveloper(false)}
            >
              Models
            </button>
            <button
              type="button"
              className={groupByDeveloper ? "on" : ""}
              onClick={() => setGroupByDeveloper(true)}
            >
              Developers
            </button>
          </div>

          <div className="relative min-w-[180px] flex-1 sm:max-w-[260px]">
            <Search className="pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[color:var(--fg-subtle)]" />
            <input
              className="ec-input pl-9"
              value={searchQuery}
              onChange={(event) => setSearchQuery(event.target.value)}
              placeholder={
                groupByDeveloper ? "Search developers…" : "Search model or developer…"
              }
            />
          </div>

          <select
            className="ec-select ml-auto shrink-0"
            value={groupByDeveloper ? developerSortBy : modelSortBy}
            onChange={(event) => {
              const value = event.target.value
              if (groupByDeveloper) setDeveloperSortBy(value as DevSort)
              else setModelSortBy(value as ModelSort)
            }}
          >
            {groupByDeveloper ? (
              <>
                <option value="coverage">Sort · Coverage</option>
                <option value="evaluated">Sort · Reported results</option>
                <option value="models">Sort · Most models</option>
                <option value="name">Sort · Name</option>
              </>
            ) : (
              <>
                <option value="benchmarks">Sort · Benchmarks</option>
                <option value="results">Sort · Reported results</option>
                <option value="released">Sort · Released</option>
                <option value="params">Sort · Parameters</option>
                <option value="name">Sort · Name</option>
              </>
            )}
          </select>
        </div>

        {/* PARAM RANGE — its own row so the rail has room to breathe.
            Sharing the toolbar with stats / search / sort squished it. */}
        {!groupByDeveloper && (
          <div className="mb-6 -mt-2">
            <ParamRangePicker
              variant="inline"
              headline="Params"
              minStep={minParamStep}
              maxStep={maxParamStep}
              onMinChange={setMinParamStep}
              onMaxChange={setMaxParamStep}
              onReset={() => {
                setMinParamStep(0)
                setMaxParamStep(PARAM_RANGE_MAX_INDEX)
              }}
              showUnknownSize={showUnknownSize}
              onShowUnknownSizeChange={setShowUnknownSize}
            />
          </div>
        )}

        {/* TABLE ---------------------------------------------------- */}
        {loading ? (
          <div className="py-24 text-center font-mono text-[11px] uppercase tracking-[0.2em] text-[color:var(--fg-subtle)]">
            Loading…
          </div>
        ) : totalCount === 0 ? (
          <div className="py-16 text-center border border-dashed border-[color:var(--border-soft)] bg-[color:var(--bg-warm)]">
            <p className="mb-4 text-base text-[color:var(--fg-muted)]">
              {groupByDeveloper
                ? "No developers found matching your filters."
                : "No models found matching your filters."}
            </p>
            <button
              type="button"
              className="btn-ec outline"
              onClick={() => {
                setSearchQuery("")
                setModelSortBy("benchmarks")
                setDeveloperSortBy("coverage")
              }}
            >
              Reset filters
            </button>
          </div>
        ) : groupByDeveloper ? (
          <DeveloperTable rows={visibleDevelopers} />
        ) : (
          <ModelTable
            rows={visibleEvaluations}
            totalBenchmarks={totalBenchmarks}
            selectedIds={selectedModelIds}
            onToggleSelect={toggleModelSelection}
            maxCompare={MAX_COMPARE_MODELS}
          />
        )}

        <InfiniteScrollSentinel
          hasMore={hasMore}
          onLoadMore={handleLoadMore}
          loadingLabel="Loading more…"
          endLabel={`Showing ${Math.min(visibleCount, totalCount).toLocaleString()} of ${totalCount.toLocaleString()} ${groupByDeveloper ? "developers" : "models"}`}
        />

        {/* COMPARE TRAY (sticky bottom) ---------------------------- */}
        {!groupByDeveloper && selectedModels.length > 0 && (
          <div className="pointer-events-none fixed inset-x-0 bottom-4 z-40 flex justify-center px-4">
            <div className="pointer-events-auto w-full max-w-5xl border border-[color:var(--fg)] bg-[color:var(--bg)] p-4 shadow-[var(--shadow-card)]">
              <div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
                <div className="space-y-2">
                  <div className="kicker">Compare tray</div>
                  <div className="flex flex-wrap gap-2">
                    {selectedModels.map((model) => (
                      <span
                        key={model.id}
                        className="inline-flex items-center gap-2 border border-[color:var(--border-soft)] bg-[color:var(--bg-warm)] px-3 py-1.5 text-sm"
                      >
                        <span className="font-medium text-[color:var(--fg)]">
                          {model.model_name}
                        </span>
                        <button
                          type="button"
                          onClick={() => toggleModelSelection(model.id)}
                          className="text-[color:var(--fg-muted)] transition-colors hover:text-[color:var(--fg)]"
                          aria-label={`Remove ${model.model_name} from compare`}
                        >
                          <X className="h-3.5 w-3.5" />
                        </button>
                      </span>
                    ))}
                  </div>
                </div>

                <div className="flex flex-wrap items-center gap-2">
                  <button
                    type="button"
                    className="btn-ec ghost"
                    onClick={() => setSelectedModelIds([])}
                  >
                    Clear
                  </button>
                  <button
                    type="button"
                    onClick={() => setCompareOpen(true)}
                    disabled={selectedModels.length < 2}
                    className="btn-ec disabled:opacity-50 disabled:cursor-not-allowed"
                  >
                    <ArrowRightLeft className="h-3.5 w-3.5" />
                    Compare {selectedModels.length}
                  </button>
                </div>
              </div>
            </div>
          </div>
        )}
      </main>

      <ModelCompareDialog
        models={selectedModels}
        open={compareOpen}
        onOpenChange={setCompareOpen}
      />
    </div>
  )
}
