"use client"

import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"
import { Flag, Pause, Play, Trophy } from "lucide-react"
import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"

// --- Types -----------------------------------------------------------------

export type RoundCell = {
  roundNumber: number // 1, 2, 3, or 4
  label: string // "70", "38 p" (Stableford) — or "" if not yet played
  // "even" is its own state (not folded into "over") -- matches this app's
  // established Golfscore-språket (DESIGN_SYSTEM.md): E gets plain neutral
  // text, no circle/square frame, never the over-par treatment.
  tone: "under" | "even" | "over" | null // null = not yet played, render as an empty/dashed cell
}

export type LeaderboardRow = {
  position: string // "1", "T2", "T5" — already formatted, ties included
  playerName: string
  todayLabel: string | null // "-2", "+4", "E", "38 p" — null = today's round not started yet
  todayIsUnderPar: boolean | null
  thruLabel: string // "F" (finished), "14" (holes played), "-" (not started)
  totalLabel: string // "-13", "+7", "146 p"
  totalIsUnderPar: boolean
  isLeader: boolean // true for every row sharing the current lead position (ties possible)
  rounds: RoundCell[] // one entry per round played SO FAR (1-4 entries, not always 4)
}

// --- Mock seed data --------------------------------------------------------
// A par-72, 4-round event that is currently 2 rounds in (round 2 live), so the
// table only shows R1 + R2 columns. Mix of finished / mid-round / not-started,
// with a tie for the lead (two T1 rows).

const t = (n: string): RoundCell["label"] => n
function completed(roundNumber: number, label: string, tone: "under" | "even" | "over"): RoundCell {
  return { roundNumber, label, tone }
}

export const MOCK_ROWS: LeaderboardRow[] = [
  {
    position: "T1",
    playerName: "Ingrid Berg",
    todayLabel: "-5",
    todayIsUnderPar: true,
    thruLabel: "F",
    totalLabel: "-8",
    totalIsUnderPar: true,
    isLeader: true,
    rounds: [completed(1, t("69"), "under"), completed(2, t("67"), "under")],
  },
  {
    position: "T1",
    playerName: "Sofie Dahl",
    todayLabel: "-2",
    todayIsUnderPar: true,
    thruLabel: "F",
    totalLabel: "-8",
    totalIsUnderPar: true,
    isLeader: true,
    rounds: [completed(1, t("66"), "under"), completed(2, t("70"), "under")],
  },
  {
    position: "3",
    playerName: "Anna Ruud",
    todayLabel: "-5",
    todayIsUnderPar: true,
    thruLabel: "14",
    totalLabel: "-7",
    totalIsUnderPar: true,
    isLeader: false,
    // Round 2 in progress -> only R1 completed.
    rounds: [completed(1, t("70"), "under")],
  },
  {
    position: "4",
    playerName: "Kari Holt",
    todayLabel: "-4",
    todayIsUnderPar: true,
    thruLabel: "12",
    totalLabel: "-5",
    totalIsUnderPar: true,
    isLeader: false,
    rounds: [completed(1, t("71"), "under")],
  },
  {
    position: "5",
    playerName: "Mette Lie",
    todayLabel: "+1",
    todayIsUnderPar: false,
    thruLabel: "16",
    totalLabel: "-3",
    totalIsUnderPar: true,
    isLeader: false,
    rounds: [completed(1, t("68"), "under")],
  },
  {
    position: "6",
    playerName: "Bjørg Sund",
    todayLabel: "-1",
    todayIsUnderPar: true,
    thruLabel: "F",
    totalLabel: "-2",
    totalIsUnderPar: true,
    isLeader: false,
    rounds: [completed(1, t("71"), "under"), completed(2, t("72"), "even")],
  },
  {
    position: "7",
    playerName: "Live Aas",
    todayLabel: "E",
    todayIsUnderPar: null,
    thruLabel: "9",
    totalLabel: "+1",
    totalIsUnderPar: false,
    isLeader: false,
    rounds: [completed(1, t("73"), "over")],
  },
  {
    position: "8",
    playerName: "Randi Vik",
    todayLabel: null, // round 2 not started yet
    todayIsUnderPar: null,
    thruLabel: "-",
    totalLabel: "+2",
    totalIsUnderPar: false,
    isLeader: false,
    rounds: [completed(1, t("74"), "over")],
  },
  {
    position: "9",
    playerName: "Tuva Moen",
    todayLabel: "+3",
    todayIsUnderPar: false,
    thruLabel: "6",
    totalLabel: "+6",
    totalIsUnderPar: false,
    isLeader: false,
    rounds: [completed(1, t("75"), "over")],
  },
]

// --- Helpers ---------------------------------------------------------------

type Tone = "under" | "over" | "even" | "none"

// Determine tone from a signed label so meaning never relies on color alone.
function toneFromLabel(label: string | null): Tone {
  if (label == null) return "none"
  const v = label.trim()
  if (v === "") return "none"
  if (v.startsWith("-")) return "under"
  if (v.startsWith("+")) return "over"
  return "even" // "E", "38 p", "146 p"
}

const toneText: Record<Tone, string> = {
  under: "text-primary",
  over: "text-brand-orange",
  even: "text-foreground",
  none: "text-muted-foreground",
}

function usePrefersReducedMotion() {
  const [reduced, setReduced] = useState(false)
  useEffect(() => {
    const mq = window.matchMedia("(prefers-reduced-motion: reduce)")
    setReduced(mq.matches)
    const onChange = () => setReduced(mq.matches)
    mq.addEventListener("change", onChange)
    return () => mq.removeEventListener("change", onChange)
  }, [])
  return reduced
}

// --- Column geometry (shared widths so header/leaders/body align) ----------

const COL = {
  pos: "w-12 lg:w-24",
  player: "min-w-[8.5rem] lg:min-w-[16rem]",
  today: "w-16 lg:w-28",
  thru: "w-14 lg:w-24",
  total: "w-20 lg:w-36",
  round: "w-14 lg:w-24",
}
// Sticky-left offset for the PLAYER column must equal the POS column width.
const PLAYER_LEFT = "left-12 lg:left-24"

const cellPad = "px-2 py-3 lg:px-5 lg:py-5"

// --- Component -------------------------------------------------------------

export function StrokePlayLeaderboard({
  rows = MOCK_ROWS,
  caption = "Resultattavle",
}: {
  rows?: LeaderboardRow[]
  caption?: string
}) {
  const reducedMotion = usePrefersReducedMotion()
  const [playing, setPlaying] = useState(false)

  const leaders = useMemo(() => rows.filter((r) => r.isLeader), [rows])
  const field = useMemo(() => rows.filter((r) => !r.isLeader), [rows])
  const roundCount = useMemo(
    () => rows.reduce((max, r) => Math.max(max, ...r.rounds.map((c) => c.roundNumber), 0), 0),
    [rows],
  )
  const roundNumbers = useMemo(
    () => Array.from({ length: roundCount }, (_, i) => i + 1),
    [roundCount],
  )
  const totalCols = 5 + roundCount

  // Measure the header height so pinned leader rows stick right beneath it.
  const theadRef = useRef<HTMLTableSectionElement>(null)
  const [headTop, setHeadTop] = useState(0)
  useLayoutEffect(() => {
    const el = theadRef.current
    if (!el) return
    const update = () => setHeadTop(el.getBoundingClientRect().height)
    update()
    const ro = new ResizeObserver(update)
    ro.observe(el)
    return () => ro.disconnect()
  }, [])

  // Auto-scroll ticker over the non-leader field.
  const scrollRef = useRef<HTMLDivElement>(null)
  useEffect(() => {
    if (!playing || reducedMotion) return
    const el = scrollRef.current
    if (!el) return
    let raf = 0
    let last = performance.now()
    const speed = 26 // px per second
    const tick = (now: number) => {
      const dt = Math.min((now - last) / 1000, 0.05)
      last = now
      const max = el.scrollHeight - el.clientHeight
      if (max > 1) {
        let next = el.scrollTop + speed * dt
        if (next >= max) next = 0 // loop back to top of field
        el.scrollTop = next
      }
      raf = requestAnimationFrame(tick)
    }
    raf = requestAnimationFrame(tick)
    return () => cancelAnimationFrame(raf)
  }, [playing, reducedMotion])

  // Any manual gesture on the list pauses the ticker (does not react to the
  // programmatic scrollTop writes above).
  useEffect(() => {
    const el = scrollRef.current
    if (!el) return
    const pause = () => setPlaying(false)
    const opts: AddEventListenerOptions = { passive: true }
    el.addEventListener("wheel", pause, opts)
    el.addEventListener("touchstart", pause, opts)
    el.addEventListener("pointerdown", pause)
    el.addEventListener("keydown", pause)
    return () => {
      el.removeEventListener("wheel", pause, opts)
      el.removeEventListener("touchstart", pause, opts)
      el.removeEventListener("pointerdown", pause)
      el.removeEventListener("keydown", pause)
    }
  }, [])

  return (
    <section
      aria-label={caption}
      className="flex w-full flex-col overflow-hidden rounded-2xl border border-border bg-card shadow-sm lg:rounded-3xl"
    >
      {/* Control bar (part of the component, not page chrome) */}
      <div className="flex flex-wrap items-center justify-between gap-3 border-b border-border bg-background px-3 py-3 lg:px-5 lg:py-4">
        <Legend />
        {!reducedMotion && (
          <Button
            type="button"
            variant={playing ? "secondary" : "outline"}
            size="lg"
            onClick={() => setPlaying((p) => !p)}
            aria-pressed={playing}
            className="h-11 gap-2 px-4 text-sm lg:h-14 lg:px-6 lg:text-base"
          >
            {playing ? (
              <Pause aria-hidden="true" className="size-4 lg:size-5" />
            ) : (
              <Play aria-hidden="true" className="size-4 lg:size-5" />
            )}
            {playing ? "Stopp rulling" : "Rull automatisk"}
          </Button>
        )}
      </div>

      {/* One scroll container drives both frozen-column horizontal scroll (narrow
          screens) and the vertical ticker. On large "storskjerm" viewports the
          cap is removed so the whole board fits with no scrolling. */}
      <div
        ref={scrollRef}
        tabIndex={0}
        aria-label="Resultatliste"
        className="relative max-h-[24rem] overflow-auto outline-none md:max-h-[32rem] lg:max-h-none lg:overflow-visible"
      >
        <table className="w-full border-separate border-spacing-0 text-left tabular-nums">
          <colgroup>
            <col className={COL.pos} />
            <col className={COL.player} />
            <col className={COL.today} />
            <col className={COL.thru} />
            <col className={COL.total} />
            {roundNumbers.map((n) => (
              <col key={n} className={COL.round} />
            ))}
          </colgroup>

          <thead ref={theadRef}>
            <tr className="bg-foreground text-background">
              <Th sticky="pos" className="z-50 text-center">
                POS<span className="sr-only"> (posisjon)</span>
              </Th>
              <Th sticky="player" className="z-50 text-left">
                PLAYER<span className="sr-only"> (spiller)</span>
              </Th>
              <Th className="text-right">
                TODAY<span className="sr-only"> (i dag)</span>
              </Th>
              <Th className="text-right">
                THRU<span className="sr-only"> (hull spilt)</span>
              </Th>
              <Th className="text-right">
                TOTAL<span className="sr-only"> (totalt mot par)</span>
              </Th>
              {roundNumbers.map((n) => (
                <Th key={n} className="text-right">
                  R{n}
                  <span className="sr-only"> (runde {n})</span>
                </Th>
              ))}
            </tr>
          </thead>

          <tbody>
            {/* Pinned leader row(s) — stick beneath the header, never in the ticker. */}
            {leaders.map((row, i) => (
              <LeaderRow
                key={`leader-${row.playerName}`}
                row={row}
                roundNumbers={roundNumbers}
                top={headTop}
                isLast={i === leaders.length - 1}
              />
            ))}

            {field.length === 0 && (
              <tr>
                <td
                  colSpan={totalCols}
                  className="px-4 py-8 text-center text-sm text-muted-foreground"
                >
                  Ingen spillere i feltet ennå.
                </td>
              </tr>
            )}

            {field.map((row, i) => (
              <FieldRow
                key={`field-${row.playerName}`}
                row={row}
                roundNumbers={roundNumbers}
                striped={i % 2 === 1}
              />
            ))}
          </tbody>
        </table>
      </div>
    </section>
  )
}

// --- Legend ----------------------------------------------------------------

function Legend() {
  return (
    <ul className="flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs font-medium text-muted-foreground lg:text-sm">
      <li className="flex items-center gap-1.5">
        <span
          aria-hidden="true"
          className="flex size-5 items-center justify-center rounded-full bg-primary/15 text-[10px] font-bold text-primary lg:size-6"
        >
          −
        </span>
        Under par
      </li>
      <li className="flex items-center gap-1.5">
        <span
          aria-hidden="true"
          className="flex size-5 items-center justify-center rounded-md bg-brand-orange/15 text-[10px] font-bold text-brand-orange lg:size-6"
        >
          +
        </span>
        Over par
      </li>
      <li className="flex items-center gap-1.5">
        <span
          aria-hidden="true"
          className="flex size-5 items-center justify-center rounded-md text-[10px] font-bold text-foreground lg:size-6"
        >
          E
        </span>
        Par
      </li>
    </ul>
  )
}

// --- Header cell -----------------------------------------------------------

function Th({
  children,
  className,
  sticky,
}: {
  children: React.ReactNode
  className?: string
  sticky?: "pos" | "player"
}) {
  return (
    <th
      scope="col"
      className={cn(
        cellPad,
        "sticky top-0 z-40 bg-foreground text-[0.7rem] font-extrabold uppercase tracking-wider text-background lg:text-base",
        sticky === "pos" && "left-0",
        sticky === "player" && PLAYER_LEFT,
        className,
      )}
    >
      {children}
    </th>
  )
}

// --- Leader row (pinned) ---------------------------------------------------

function LeaderRow({
  row,
  roundNumbers,
  top,
  isLast,
}: {
  row: LeaderboardRow
  roundNumbers: number[]
  top: number
  isLast: boolean
}) {
  const stickyStyle = { top }
  const rowBg = "bg-gold text-gold-foreground"
  const border = isLast ? "border-b-2 border-gold-foreground/30" : "border-b border-gold-foreground/15"

  return (
    <tr className={rowBg}>
      {/* POS + trophy (sticky both top and left, above field + own row) */}
      <td
        style={stickyStyle}
        className={cn(cellPad, rowBg, border, "sticky left-0 z-30 text-center align-middle")}
      >
        <div className="flex items-center justify-center gap-1.5">
          <Trophy aria-hidden="true" className="size-4 shrink-0 lg:size-6" />
          <span className="text-sm font-extrabold lg:text-3xl">{row.position}</span>
          <span className="sr-only">, leder</span>
        </div>
      </td>

      {/* PLAYER */}
      <td
        style={stickyStyle}
        className={cn(cellPad, rowBg, border, "sticky z-30 text-left", PLAYER_LEFT)}
      >
        <span className="flex items-center gap-2 text-sm font-extrabold lg:text-2xl">
          <Flag aria-hidden="true" className="size-3.5 shrink-0 lg:size-5" />
          <span className="truncate">{row.playerName}</span>
        </span>
      </td>

      <ValueTd style={stickyStyle} rowBg={rowBg} border={border} label={row.todayLabel} emphasis="today" leader />
      <ThruTd style={stickyStyle} rowBg={rowBg} border={border} thru={row.thruLabel} leader />
      <ValueTd style={stickyStyle} rowBg={rowBg} border={border} label={row.totalLabel} emphasis="total" leader />

      {roundNumbers.map((n) => {
        const cell = row.rounds.find((c) => c.roundNumber === n)
        return <RoundTd key={n} style={stickyStyle} rowBg={rowBg} border={border} cell={cell} leader />
      })}
    </tr>
  )
}

// --- Field row -------------------------------------------------------------

function FieldRow({
  row,
  roundNumbers,
  striped,
}: {
  row: LeaderboardRow
  roundNumbers: number[]
  striped: boolean
}) {
  const rowBg = striped ? "bg-muted/40" : "bg-card"
  const border = "border-b border-border"

  return (
    <tr className={rowBg}>
      <td className={cn(cellPad, rowBg, border, "sticky left-0 z-10 text-center")}>
        <span className="text-sm font-bold text-foreground lg:text-2xl">{row.position}</span>
      </td>
      <td className={cn(cellPad, rowBg, border, "sticky z-10 text-left", PLAYER_LEFT)}>
        <span className="block truncate text-sm font-semibold text-foreground lg:text-xl">
          {row.playerName}
        </span>
      </td>

      <ValueTd rowBg={rowBg} border={border} label={row.todayLabel} emphasis="today" />
      <ThruTd rowBg={rowBg} border={border} thru={row.thruLabel} />
      <ValueTd rowBg={rowBg} border={border} label={row.totalLabel} emphasis="total" />

      {roundNumbers.map((n) => {
        const cell = row.rounds.find((c) => c.roundNumber === n)
        return <RoundTd key={n} rowBg={rowBg} border={border} cell={cell} />
      })}
    </tr>
  )
}

// --- Value cell (TODAY / TOTAL) --------------------------------------------

function ValueTd({
  label,
  emphasis,
  rowBg,
  border,
  style,
  leader,
}: {
  label: string | null
  emphasis: "today" | "total"
  rowBg: string
  border: string
  style?: React.CSSProperties
  leader?: boolean
}) {
  const tone = toneFromLabel(label)
  const isTotal = emphasis === "total"

  // On the gold leader band, keep values readable on gold rather than tinting
  // them green/orange (which fails contrast on gold); the sign still encodes tone.
  const color = leader ? "text-gold-foreground" : toneText[tone]

  const size = isTotal
    ? "text-base font-extrabold lg:text-4xl"
    : "text-sm font-bold lg:text-2xl"

  return (
    <td style={style} className={cn(cellPad, rowBg, border, "text-right", leader && "sticky z-20")}>
      {label == null ? (
        <span className="text-sm text-muted-foreground lg:text-xl" aria-label="ikke startet">
          –
        </span>
      ) : (
        <span className={cn(size, color)}>{label}</span>
      )}
    </td>
  )
}

// --- THRU cell -------------------------------------------------------------

function ThruTd({
  thru,
  rowBg,
  border,
  style,
  leader,
}: {
  thru: string
  rowBg: string
  border: string
  style?: React.CSSProperties
  leader?: boolean
}) {
  const finished = thru === "F"
  const notStarted = thru === "-"
  const color = leader ? "text-gold-foreground" : "text-foreground"

  return (
    <td style={style} className={cn(cellPad, rowBg, border, "text-right", leader && "sticky z-20")}>
      {notStarted ? (
        <span className="text-sm text-muted-foreground lg:text-xl">–</span>
      ) : finished ? (
        <span className={cn("text-sm font-bold lg:text-xl", color)}>
          F<span className="sr-only"> (ferdig)</span>
        </span>
      ) : (
        <span className={cn("text-sm font-semibold lg:text-xl", leader ? "text-gold-foreground" : "text-muted-foreground")}>
          {thru}
          <span className="sr-only"> hull spilt</span>
        </span>
      )}
    </td>
  )
}

// --- Round cell (Rn) -------------------------------------------------------
// Shape encodes tone in addition to color: under-par = round pill, over-par =
// square pill — so meaning survives without color perception.

function RoundTd({
  cell,
  rowBg,
  border,
  style,
  leader,
}: {
  cell?: RoundCell
  rowBg: string
  border: string
  style?: React.CSSProperties
  leader?: boolean
}) {
  const played = cell && cell.label !== "" && cell.tone !== null

  return (
    <td style={style} className={cn(cellPad, rowBg, border, "text-right", leader && "sticky z-20")}>
      {!played ? (
        <span className="text-sm text-muted-foreground lg:text-xl" aria-label="ikke spilt">
          –
        </span>
      ) : cell!.tone === "even" ? (
        // "E" er alltid ren tekst, ingen ramme -- samme regel som resten av
        // appens Golfscore-språk (DESIGN_SYSTEM.md), aldri over-par-behandling.
        <span className={cn("text-sm font-bold lg:text-lg", leader ? "text-gold-foreground" : "text-foreground")}>
          {cell!.label}
          <span className="sr-only"> par</span>
        </span>
      ) : (
        <span
          className={cn(
            "inline-flex min-w-8 items-center justify-center px-1.5 py-0.5 text-sm font-bold lg:min-w-12 lg:px-2 lg:py-1 lg:text-lg",
            // Shape encodes tone (circle=under, square=over) so meaning survives
            // without color. On the gold leader band, use a solid opaque chip so
            // the low-opacity tint doesn't wash out against gold.
            cell!.tone === "under" ? "rounded-full" : "rounded-md",
            leader
              ? cn("bg-card", cell!.tone === "under" ? "text-primary" : "text-brand-orange")
              : cell!.tone === "under"
                ? "bg-primary/15 text-primary"
                : "bg-brand-orange/15 text-brand-orange",
          )}
        >
          {cell!.label}
          <span className="sr-only">{cell!.tone === "under" ? " under par" : " over par"}</span>
        </span>
      )}
    </td>
  )
}
