"use client"

import { useEffect, useMemo, useRef, useState } from "react"
import { ChevronDown, ChevronUp, ExternalLink, Loader2, Trash2, TriangleAlert } from "lucide-react"

export type TournamentPlayerRow = {
  participantId: string
  playerId: string
  playerName: string
  handicapSnapshot: number | null
  classId: string | null
  statLevel: "strokes_only" | "strokes_and_putts" | "full"
  status: "active" | "dsq" | "rtd" | "dnf" | "dns"
  gender: "m" | "f" | "x" | null
  birthDate: string | null // ISO "YYYY-MM-DD", null if unknown
}

export type ClassOption = { id: string; name: string }

export type TournamentPlayersRound = {
  id: string
  label: string // e.g. "Runde 1"
  tees: { id: string; name: string }[]
}

export type TournamentPlayersCell = {
  assigned: boolean
  teeId: string | null
  teeName: string | null
}

export type TournamentPlayersTableProps = {
  players: TournamentPlayerRow[]
  classes: ClassOption[]
  rounds: TournamentPlayersRound[]
  // cells[roundId][participantId]
  cells: Record<string, Record<string, TournamentPlayersCell>>
  organizationId: string
  onSetHandicap: (participantId: string, value: number | null) => void
  onSetClass: (participantId: string, classId: string | null) => void
  onSetStatLevel: (participantId: string, statLevel: TournamentPlayerRow["statLevel"]) => void
  onSetStatus: (participantId: string, status: TournamentPlayerRow["status"]) => void
  onRemoveParticipant: (participantId: string) => void
  onChangeGender: (playerId: string, gender: "m" | "f" | "x") => void
  onChangeBirthDate: (playerId: string, birthDate: string) => void
  onToggleRound: (roundId: string, participantId: string, checked: boolean, teeId: string) => void
  onBulkToggleRound: (roundId: string, checked: boolean, teeId: string) => void
  onChangeRoundTee: (roundId: string, participantId: string, teeId: string) => void
  busyRoundIds: string[]
  errors: string[]
}

const STAT_LEVELS: { label: string; value: TournamentPlayerRow["statLevel"] }[] = [
  { label: "Kun slag", value: "strokes_only" },
  { label: "Slag og putt", value: "strokes_and_putts" },
  { label: "Fullt", value: "full" },
]

const STATUSES: { label: string; value: TournamentPlayerRow["status"] }[] = [
  { label: "Aktiv", value: "active" },
  { label: "DSQ", value: "dsq" },
  { label: "RTD", value: "rtd" },
  { label: "DNF", value: "dnf" },
  { label: "DNS", value: "dns" },
]

// Spreadsheet cell control: flat, fills the whole cell, no border of its own.
// The grid lines define the structure; controls only light up on hover/focus.
const CELL =
  "h-full w-full border-0 bg-transparent px-2 py-1.5 text-[13px] leading-tight text-foreground outline-none transition-colors hover:bg-accent/40 focus-visible:bg-background focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"

// Compact header control (round default-tee select).
const HEAD_CELL =
  "h-7 w-full rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground outline-none transition-colors hover:border-primary/40 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"

// Shared cell frame: thin grid lines, zero padding (the control supplies it).
const TD = "border-b border-r border-border p-0 align-middle"
const TH =
  "border-b border-r border-border bg-muted px-2 py-2 text-left text-[11px] font-bold uppercase tracking-wide text-muted-foreground"

function computeAge(birthDate: string): number | null {
  const birth = new Date(birthDate)
  if (Number.isNaN(birth.getTime())) return null
  const now = new Date()
  let age = now.getFullYear() - birth.getFullYear()
  const monthDiff = now.getMonth() - birth.getMonth()
  if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < birth.getDate())) {
    age -= 1
  }
  return age
}

// ── Sortering (2026-08-20) ──────────────────────────────────────────────
// Kun disse syv kolonnene er sorterbare -- runde-kolonnene (deltar+utslag)
// og handlingskolonnen er det bevisst IKKE, se V0-prompten.
type SortKey = "name" | "hcp" | "className" | "statLevel" | "status" | "gender" | "age"
type SortDir = "asc" | "desc"
type SortState = { key: SortKey; dir: SortDir } | null

const STAT_LEVEL_RANK: Record<TournamentPlayerRow["statLevel"], number> = {
  strokes_only: 0,
  strokes_and_putts: 1,
  full: 2,
}
const STATUS_RANK: Record<TournamentPlayerRow["status"], number> = {
  active: 0,
  dsq: 1,
  rtd: 2,
  dnf: 3,
  dns: 4,
}
const GENDER_SORT_LABEL: Record<"m" | "f" | "x", string> = { f: "Kvinne", m: "Mann", x: "Annet" }

// Sammenlignbar verdi per sorteringskolonne. Null sorteres alltid sist
// (håndtert i compareRows), uansett retning.
function sortValue(p: TournamentPlayerRow, key: SortKey, classNameById: Record<string, string>): string | number | null {
  switch (key) {
    case "name":
      return p.playerName.toLocaleLowerCase("nb")
    case "hcp":
      return p.handicapSnapshot
    case "className": {
      const name = p.classId ? classNameById[p.classId] : undefined
      return name ? name.toLocaleLowerCase("nb") : null
    }
    case "statLevel":
      return STAT_LEVEL_RANK[p.statLevel]
    case "status":
      return STATUS_RANK[p.status]
    case "gender":
      return p.gender ? GENDER_SORT_LABEL[p.gender].toLocaleLowerCase("nb") : null
    case "age":
      return p.birthDate ? computeAge(p.birthDate) : null
  }
}

function compareRows(
  a: TournamentPlayerRow,
  b: TournamentPlayerRow,
  key: SortKey,
  dir: SortDir,
  classNameById: Record<string, string>,
): number {
  const av = sortValue(a, key, classNameById)
  const bv = sortValue(b, key, classNameById)
  if (av === null && bv === null) return 0
  if (av === null) return 1
  if (bv === null) return -1
  const res = typeof av === "number" && typeof bv === "number" ? av - bv : String(av).localeCompare(String(bv), "nb")
  return dir === "asc" ? res : -res
}

function SortableHeader({
  sortKey,
  label,
  sort,
  onSort,
  align,
  sticky,
}: {
  sortKey: SortKey
  label: string
  sort: SortState
  onSort: (key: SortKey) => void
  align?: "right"
  sticky?: boolean
}) {
  const active = sort?.key === sortKey
  const dir = active ? sort.dir : null
  const ariaSort = active ? (dir === "asc" ? "ascending" : "descending") : "none"
  return (
    <th
      scope="col"
      aria-sort={ariaSort}
      className={`border-b border-r border-border bg-muted p-0 ${sticky ? "sticky left-0 top-0 z-30" : "sticky top-0 z-20"}`}
    >
      <button
        type="button"
        onClick={() => onSort(sortKey)}
        aria-label={`Sorter etter ${label}${active ? (dir === "asc" ? ", stigende" : ", synkende") : ""}`}
        className={`flex h-full min-h-9 w-full items-center gap-1 px-2 py-2 text-[11px] font-bold uppercase tracking-wide transition-colors hover:bg-accent/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring ${
          active ? "text-primary" : "text-muted-foreground"
        } ${align === "right" ? "justify-end text-right" : "justify-start text-left"}`}
      >
        <span className="truncate">{label}</span>
        {active && dir === "asc" && <ChevronUp aria-hidden="true" className="size-3.5 shrink-0" />}
        {active && dir === "desc" && <ChevronDown aria-hidden="true" className="size-3.5 shrink-0" />}
      </button>
    </th>
  )
}

export function TournamentPlayersTable({
  players,
  classes,
  rounds,
  cells,
  organizationId,
  onSetHandicap,
  onSetClass,
  onSetStatLevel,
  onSetStatus,
  onRemoveParticipant,
  onChangeGender,
  onChangeBirthDate,
  onToggleRound,
  onBulkToggleRound,
  onChangeRoundTee,
  busyRoundIds,
  errors,
}: TournamentPlayersTableProps) {
  const showClassColumn = classes.length > 0

  // Local-only: each round's "Standardutslag" (default tee) selection.
  // Resets to each round's first tee whenever the round set changes.
  const [defaultTees, setDefaultTees] = useState<Record<string, string>>({})
  useEffect(() => {
    const next: Record<string, string> = {}
    for (const round of rounds) {
      next[round.id] = round.tees[0]?.id ?? ""
    }
    setDefaultTees(next)
  }, [rounds])

  // Sortering: stigende -> synkende -> tilbake til opprinnelig rekkefølge.
  const [sort, setSort] = useState<SortState>(null)
  const cycleSort = (key: SortKey) => {
    setSort((prev) => {
      if (!prev || prev.key !== key) return { key, dir: "asc" }
      if (prev.dir === "asc") return { key, dir: "desc" }
      return null
    })
  }
  const classNameById = useMemo(() => Object.fromEntries(classes.map((c) => [c.id, c.name])), [classes])
  const sortedPlayers = useMemo(() => {
    if (!sort) return players
    return [...players].sort((a, b) => compareRows(a, b, sort.key, sort.dir, classNameById))
  }, [players, sort, classNameById])

  return (
    <div className="flex flex-col gap-4">
      {errors.length > 0 && (
        <div
          role="alert"
          className="flex flex-col gap-1 rounded-xl border border-destructive/30 bg-destructive/10 p-4 text-destructive"
        >
          <div className="flex items-center gap-2">
            <TriangleAlert aria-hidden="true" className="size-5 shrink-0" />
            <h3 className="text-base font-bold">Noen endringer feilet</h3>
          </div>
          <ul className="pl-7 text-sm leading-relaxed">
            {errors.map((err, i) => (
              <li key={i} className="list-disc">
                {err}
              </li>
            ))}
          </ul>
        </div>
      )}

      <div className="overflow-x-auto rounded-xl border border-border bg-card shadow-sm shadow-black/5">
        {/* Fluid layout: the table fills the available width instead of leaving
            dead space and pushing round columns off-screen. */}
        <table className="w-full border-collapse text-left [&_td]:last:border-r-0 [&_th]:last:border-r-0">
          <colgroup>
            <col className="w-[13rem]" />
            <col className="w-[5.5rem]" />
            {showClassColumn && <col className="w-[9rem]" />}
            <col className="w-[9rem]" />
            <col className="w-[7rem]" />
            <col className="w-[7rem]" />
            <col className="w-[10rem]" />
            {rounds.map((r) => (
              <col key={r.id} />
            ))}
            <col className="w-[5.5rem]" />
          </colgroup>
          <thead>
            <tr>
              <SortableHeader sortKey="name" label="Spiller" sort={sort} onSort={cycleSort} sticky />
              <SortableHeader sortKey="hcp" label="Hcp" sort={sort} onSort={cycleSort} align="right" />
              {showClassColumn && (
                <SortableHeader sortKey="className" label="Klasse" sort={sort} onSort={cycleSort} />
              )}
              <SortableHeader sortKey="statLevel" label="Statistikknivå" sort={sort} onSort={cycleSort} />
              <SortableHeader sortKey="status" label="Status" sort={sort} onSort={cycleSort} />
              <SortableHeader sortKey="gender" label="Kjønn" sort={sort} onSort={cycleSort} />
              <SortableHeader sortKey="age" label="Alder" sort={sort} onSort={cycleSort} />
              {rounds.map((round) => {
                const busy = busyRoundIds.includes(round.id)
                const assignedCount = players.filter(
                  (p) => cells[round.id]?.[p.participantId]?.assigned,
                ).length
                const allAssigned = players.length > 0 && assignedCount === players.length
                const someAssigned = assignedCount > 0 && assignedCount < players.length
                return (
                  <th
                    key={round.id}
                    scope="col"
                    className={`${TH} sticky top-0 z-20 min-w-[9.5rem] align-top`}
                  >
                    <div className="flex flex-col gap-1.5">
                      <div className="flex items-center justify-between gap-2">
                        <span>{round.label}</span>
                        {busy && (
                          <span className="flex items-center gap-1 normal-case text-primary">
                            <Loader2 aria-hidden="true" className="size-3.5 animate-spin" />
                            <span className="text-[10px] font-semibold">Oppdaterer</span>
                          </span>
                        )}
                      </div>
                      <select
                        aria-label={`Standardutslag for ${round.label}`}
                        className={HEAD_CELL}
                        value={defaultTees[round.id] ?? ""}
                        disabled={busy}
                        onChange={(e) =>
                          setDefaultTees((prev) => ({ ...prev, [round.id]: e.target.value }))
                        }
                      >
                        {round.tees.map((tee) => (
                          <option key={tee.id} value={tee.id}>
                            {tee.name}
                          </option>
                        ))}
                      </select>
                      <BulkCheckbox
                        checked={allAssigned}
                        indeterminate={someAssigned}
                        disabled={busy || players.length === 0}
                        label="Velg alle"
                        onChange={(next) =>
                          onBulkToggleRound(round.id, next, defaultTees[round.id] ?? round.tees[0]?.id ?? "")
                        }
                      />
                    </div>
                  </th>
                )
              })}
              <th scope="col" className={`${TH} sticky top-0 z-20 text-center`}>
                <span className="sr-only">Handlinger</span>
                <span aria-hidden="true">···</span>
              </th>
            </tr>
          </thead>
          <tbody>
            {sortedPlayers.map((player) => (
              <PlayerRow
                key={player.participantId}
                player={player}
                classes={classes}
                showClassColumn={showClassColumn}
                rounds={rounds}
                cells={cells}
                organizationId={organizationId}
                defaultTees={defaultTees}
                busyRoundIds={busyRoundIds}
                onSetHandicap={onSetHandicap}
                onSetClass={onSetClass}
                onSetStatLevel={onSetStatLevel}
                onSetStatus={onSetStatus}
                onRemoveParticipant={onRemoveParticipant}
                onChangeGender={onChangeGender}
                onChangeBirthDate={onChangeBirthDate}
                onToggleRound={onToggleRound}
                onChangeRoundTee={onChangeRoundTee}
              />
            ))}
          </tbody>
        </table>
      </div>
    </div>
  )
}

function BulkCheckbox({
  checked,
  indeterminate,
  disabled,
  label,
  onChange,
}: {
  checked: boolean
  indeterminate: boolean
  disabled: boolean
  label: string
  onChange: (next: boolean) => void
}) {
  const ref = useRef<HTMLInputElement>(null)
  useEffect(() => {
    if (ref.current) ref.current.indeterminate = indeterminate && !checked
  }, [indeterminate, checked])
  return (
    <label className="flex items-center gap-1.5 normal-case">
      <input
        ref={ref}
        type="checkbox"
        checked={checked}
        disabled={disabled}
        onChange={(e) => onChange(e.target.checked)}
        className="size-4 rounded border-border text-primary outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
      />
      <span className="text-[10px] font-semibold text-muted-foreground">{label}</span>
    </label>
  )
}

function PlayerRow({
  player,
  classes,
  showClassColumn,
  rounds,
  cells,
  organizationId,
  defaultTees,
  busyRoundIds,
  onSetHandicap,
  onSetClass,
  onSetStatLevel,
  onSetStatus,
  onRemoveParticipant,
  onChangeGender,
  onChangeBirthDate,
  onToggleRound,
  onChangeRoundTee,
}: {
  player: TournamentPlayerRow
  classes: ClassOption[]
  showClassColumn: boolean
  rounds: TournamentPlayersRound[]
  cells: Record<string, Record<string, TournamentPlayersCell>>
  organizationId: string
  defaultTees: Record<string, string>
  busyRoundIds: string[]
  onSetHandicap: (participantId: string, value: number | null) => void
  onSetClass: (participantId: string, classId: string | null) => void
  onSetStatLevel: (participantId: string, statLevel: TournamentPlayerRow["statLevel"]) => void
  onSetStatus: (participantId: string, status: TournamentPlayerRow["status"]) => void
  onRemoveParticipant: (participantId: string) => void
  onChangeGender: (playerId: string, gender: "m" | "f" | "x") => void
  onChangeBirthDate: (playerId: string, birthDate: string) => void
  onToggleRound: (roundId: string, participantId: string, checked: boolean, teeId: string) => void
  onChangeRoundTee: (roundId: string, participantId: string, teeId: string) => void
}) {
  // Handicap is a local draft committed on blur, not per-keystroke.
  const [hcpDraft, setHcpDraft] = useState(
    player.handicapSnapshot === null ? "" : String(player.handicapSnapshot),
  )
  useEffect(() => {
    setHcpDraft(player.handicapSnapshot === null ? "" : String(player.handicapSnapshot))
  }, [player.handicapSnapshot])

  const age = player.birthDate ? computeAge(player.birthDate) : null
  const statusAbnormal = player.status !== "active"

  function commitHcp() {
    const trimmed = hcpDraft.trim()
    if (trimmed === "") {
      onSetHandicap(player.participantId, null)
      return
    }
    // Komma-desimal (norsk skrivemåte, "12,4") skal fungere likt som punktum
    // -- 2026-08-19, bruker: "jeg vil at det skal være irrelevant".
    const num = Number(trimmed.replace(",", "."))
    if (Number.isNaN(num)) {
      // Revert bad input back to the last known good value.
      setHcpDraft(player.handicapSnapshot === null ? "" : String(player.handicapSnapshot))
      return
    }
    const clamped = Math.min(54, Math.max(-10, num))
    onSetHandicap(player.participantId, clamped)
  }

  return (
    <tr className="group h-9">
      <th
        scope="row"
        className={`${TD} sticky left-0 z-10 bg-card px-2 py-1.5 text-left text-[13px] font-semibold text-foreground group-hover:bg-accent/30`}
      >
        {player.playerName}
      </th>

      <td className={TD}>
        <input
          // type="text" (ikke "number") -- native number-inputs blokkerer
          // komma-tegnet i de fleste nettlesere FØR det når JS i det hele
          // tatt, uansett hva onBlur-parsingen gjør. inputMode="decimal"
          // beholdes for riktig mobiltastatur. 2026-08-19.
          inputMode="decimal"
          aria-label={`Handicap for ${player.playerName}`}
          value={hcpDraft}
          onChange={(e) => setHcpDraft(e.target.value)}
          onBlur={commitHcp}
          className={`${CELL} text-right tabular-nums`}
        />
      </td>

      {showClassColumn && (
        <td className={TD}>
          <select
            aria-label={`Klasse for ${player.playerName}`}
            className={CELL}
            value={player.classId ?? ""}
            onChange={(e) => onSetClass(player.participantId, e.target.value === "" ? null : e.target.value)}
          >
            <option value="">Ingen klasse</option>
            {classes.map((c) => (
              <option key={c.id} value={c.id}>
                {c.name}
              </option>
            ))}
          </select>
        </td>
      )}

      <td className={TD}>
        <select
          aria-label={`Statistikknivå for ${player.playerName}`}
          className={CELL}
          value={player.statLevel}
          onChange={(e) => onSetStatLevel(player.participantId, e.target.value as TournamentPlayerRow["statLevel"])}
        >
          {STAT_LEVELS.map((s) => (
            <option key={s.value} value={s.value}>
              {s.label}
            </option>
          ))}
        </select>
      </td>

      <td className={TD}>
        <select
          aria-label={`Status for ${player.playerName}`}
          className={`${CELL} ${statusAbnormal ? "bg-destructive/10 font-semibold text-destructive hover:bg-destructive/15" : ""}`}
          value={player.status}
          onChange={(e) => onSetStatus(player.participantId, e.target.value as TournamentPlayerRow["status"])}
        >
          {STATUSES.map((s) => (
            <option key={s.value} value={s.value}>
              {s.label}
            </option>
          ))}
        </select>
      </td>

      <td className={TD}>
        <select
          aria-label={`Kjønn for ${player.playerName}`}
          className={CELL}
          value={player.gender ?? ""}
          onChange={(e) => onChangeGender(player.playerId, e.target.value as "m" | "f" | "x")}
        >
          <option value="" disabled>
            Ikke satt
          </option>
          <option value="m">Mann</option>
          <option value="f">Kvinne</option>
          <option value="x">Annet</option>
        </select>
      </td>

      <td className={TD}>
        <div className="flex items-center">
          <input
            type="date"
            aria-label={`Fødselsdato for ${player.playerName}`}
            className={`${CELL} min-w-0`}
            value={player.birthDate ?? ""}
            onChange={(e) => onChangeBirthDate(player.playerId, e.target.value)}
          />
          <span className="shrink-0 whitespace-nowrap px-1.5 text-[11px] font-semibold tabular-nums text-muted-foreground">
            {age !== null ? `${age} år` : "Ukjent"}
          </span>
        </div>
      </td>

      {rounds.map((round) => {
        const cell = cells[round.id]?.[player.participantId]
        const assigned = cell?.assigned ?? false
        const busy = busyRoundIds.includes(round.id)
        const previewTeeId = cell?.teeId ?? defaultTees[round.id] ?? round.tees[0]?.id ?? ""
        return (
          <td key={round.id} className={TD}>
            <div className="flex items-center gap-1 px-2">
              <label
                className="flex shrink-0 cursor-pointer items-center py-2 pr-2"
                title={assigned ? "Spiller runden" : "Spiller ikke runden"}
              >
                <input
                  type="checkbox"
                  checked={assigned}
                  disabled={busy}
                  aria-label={`${player.playerName} spiller ${round.label}`}
                  onChange={(e) => {
                    const checked = e.target.checked
                    const teeId = checked ? previewTeeId : cell?.teeId ?? previewTeeId
                    onToggleRound(round.id, player.participantId, checked, teeId)
                  }}
                  className="size-4 shrink-0 rounded border-border text-primary outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
                />
              </label>
              <span aria-hidden="true" className="h-5 w-px shrink-0 bg-border" />
              <select
                aria-label={`Utslag for ${player.playerName} i ${round.label}`}
                className={`h-7 min-w-0 flex-1 rounded-md border px-1.5 text-xs outline-none transition-colors hover:border-primary/40 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 ${
                  assigned
                    ? "border-border bg-background text-foreground"
                    : "border-dashed border-border/70 bg-transparent text-muted-foreground"
                }`}
                value={previewTeeId}
                disabled={busy}
                onChange={(e) => {
                  if (assigned) {
                    onChangeRoundTee(round.id, player.participantId, e.target.value)
                  } else {
                    onToggleRound(round.id, player.participantId, true, e.target.value)
                  }
                }}
              >
                {round.tees.map((tee) => (
                  <option key={tee.id} value={tee.id}>
                    {tee.name}
                  </option>
                ))}
              </select>
            </div>
          </td>
        )
      })}

      <td className={TD}>
        <div className="flex items-center justify-center gap-0.5 px-1">
          <a
            href={`/organizations/${organizationId}/players?highlight=${player.playerId}`}
            aria-label={`Rediger ${player.playerName} i spillerpoolen`}
            title="Rediger i spillerpoolen"
            className="inline-flex size-7 items-center justify-center rounded-md text-muted-foreground outline-none transition-colors hover:bg-accent/60 hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
          >
            <ExternalLink aria-hidden="true" className="size-4" />
          </a>
          <button
            type="button"
            aria-label={`Fjern ${player.playerName}`}
            title="Fjern spiller"
            onClick={() => onRemoveParticipant(player.participantId)}
            className="inline-flex size-7 items-center justify-center rounded-md text-muted-foreground outline-none transition-colors hover:bg-destructive/10 hover:text-destructive focus-visible:ring-2 focus-visible:ring-ring active:scale-95"
          >
            <Trash2 aria-hidden="true" className="size-4" />
          </button>
        </div>
      </td>
    </tr>
  )
}
