"use client"

// Hull-for-hull-registrering for en frittstående runde (ADR-033).
// Presentasjon fra V0, datalag skrevet om fra mock til ekte fetch/PATCH.
//
// VIKTIG kontraktsdetalj (bekreftet i scratch): hull-PATCH-endepunktet er
// IKKE et ekte delvis-PATCH -- det skriver ALLE felt ved hvert kall. Derfor
// slår updateStat() alltid sammen med gjeldende hull-data FØR den sender,
// aldri kun det ene feltet som ble endret.

import type React from "react"
import { useCallback, useEffect, useState } from "react"
import Link from "next/link"
import {
  ArrowLeft,
  CalendarDays,
  Check,
  ChevronDown,
  ChevronLeft,
  ChevronRight,
  Flag,
  MapPin,
  Minus,
  Plus,
  Trophy,
  X,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { cn } from "@/lib/utils"

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

type Gender = "male" | "female" | "other"
type ApiGender = "m" | "f" | "x"
type TeeShot = "left" | "fairway" | "right"
type Approach = "left" | "short" | "hit" | "long" | "right"

type Player = {
  id: string
  name: string
  gender: Gender
  hcp: number | null
  isSelf: boolean
  countsForHandicap: boolean
  scoreDifferential: number | null
}

type HoleStat = {
  played: boolean
  strokes: number | null
  putts: number | null
  club: string
  teeShot: TeeShot | null
  approach: Approach | null
  chip: number
  bunker: number
  penalty: number
  firstPuttDistance: string
}

type Hole = {
  holeNumber: number
  par: number
  index: number
}

function emptyStat(): HoleStat {
  return {
    played: false,
    strokes: null,
    putts: null,
    club: "",
    teeShot: null,
    approach: null,
    chip: 0,
    bunker: 0,
    penalty: 0,
    firstPuttDistance: "",
  }
}

function statKey(playerId: string, holeNumber: number) {
  return `${playerId}:${holeNumber}`
}

function apiGenderToUi(g: ApiGender): Gender {
  return g === "m" ? "male" : g === "f" ? "female" : "other"
}
function uiGenderToApi(g: Gender): ApiGender {
  return g === "male" ? "m" : g === "female" ? "f" : "x"
}

const dateFormatter = new Intl.DateTimeFormat("no-NO", { day: "numeric", month: "long", year: "numeric" })
function formatDate(value: string) {
  const parsed = new Date(value)
  if (Number.isNaN(parsed.getTime())) return value
  return dateFormatter.format(parsed)
}

// --- API-typer ---------------------------------------------------------------

type ApiParticipant = {
  id: string
  user_id: string | null
  guest_name: string | null
  is_owner: boolean
  gender: ApiGender
  handicap_index_snapshot: number | null
  course_handicap_snapshot: number | null
  counts_for_handicap: boolean
  score_differential: number | null
}

type ApiRound = {
  id: string
  course_name_snapshot: string
  tee_name_snapshot: string
  played_at: string
  start_hole: number
  holes_planned: number
  completed_at: string | null
  participants: ApiParticipant[]
}

type ApiHole = {
  hole_number: number
  par: number
  stroke_index: number
  played: boolean
  score: number | null
  putts: number | null
  club_off_tee: string | null
  tee_shot_result: TeeShot | null
  approach_result: Approach | null
  chip_count: number | null
  bunker_shot_count: number | null
  penalty_strokes: number | null
  first_putt_distance_m: number | null
}

function apiHoleToStat(h: ApiHole): HoleStat {
  return {
    played: h.played,
    strokes: h.score,
    putts: h.putts,
    club: h.club_off_tee ?? "",
    teeShot: h.tee_shot_result,
    approach: h.approach_result,
    chip: h.chip_count ?? 0,
    bunker: h.bunker_shot_count ?? 0,
    penalty: h.penalty_strokes ?? 0,
    firstPuttDistance: h.first_putt_distance_m !== null ? String(h.first_putt_distance_m) : "",
  }
}

function statToPatchBody(s: HoleStat) {
  return {
    played: s.played,
    score: s.strokes,
    putts: s.putts,
    club_off_tee: s.club.trim() === "" ? null : s.club,
    tee_shot_result: s.teeShot,
    approach_result: s.approach,
    chip_count: s.chip,
    bunker_shot_count: s.bunker,
    penalty_strokes: s.penalty,
    first_putt_distance_m: s.firstPuttDistance.trim() === "" ? null : Number(s.firstPuttDistance.replace(",", ".")),
  }
}

function playerLabel(p: ApiParticipant): string {
  return p.is_owner ? "Deg" : p.guest_name ?? "Gjest"
}

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

export function RoundDetail({ roundId }: { roundId: string }) {
  const [round, setRound] = useState<ApiRound | null>(null)
  const [error, setError] = useState<string | null>(null)
  const [activePlayerId, setActivePlayerId] = useState<string | null>(null)
  const [holesByParticipant, setHolesByParticipant] = useState<Record<string, ApiHole[]>>({})
  const [currentHole, setCurrentHole] = useState<number>(1)
  const [detailsOpen, setDetailsOpen] = useState(false)
  const [showAddGuest, setShowAddGuest] = useState(false)
  const [completing, setCompleting] = useState(false)

  const loadRound = useCallback(async () => {
    try {
      const res = await fetch(`/rounds/${roundId}`, { credentials: "include" })
      if (res.status === 403 || res.status === 404) {
        setError("Denne runden finnes ikke, eller du har ikke tilgang til den.")
        return
      }
      if (!res.ok) throw new Error(`round: ${res.status}`)
      const data: ApiRound = await res.json()
      setRound(data)
      setActivePlayerId((prev) => prev ?? data.participants.find((p) => p.is_owner)?.id ?? data.participants[0]?.id ?? null)
      setCurrentHole((prev) => prev || data.start_hole)
    } catch {
      setError("Klarte ikke å hente runden. Prøv igjen om litt.")
    }
  }, [roundId])

  useEffect(() => {
    void loadRound()
  }, [loadRound])

  const loadHoles = useCallback(
    async (participantId: string) => {
      const res = await fetch(`/rounds/${roundId}/participants/${participantId}/holes`, { credentials: "include" })
      if (!res.ok) return
      const data: ApiHole[] = await res.json()
      setHolesByParticipant((prev) => ({ ...prev, [participantId]: data }))
    },
    [roundId],
  )

  useEffect(() => {
    if (activePlayerId && !holesByParticipant[activePlayerId]) {
      void loadHoles(activePlayerId)
    }
  }, [activePlayerId, holesByParticipant, loadHoles])

  const completed = round?.completed_at != null
  const readOnly = completed

  const players: Player[] =
    round?.participants.map((p) => ({
      id: p.id,
      name: playerLabel(p),
      gender: apiGenderToUi(p.gender),
      hcp: p.handicap_index_snapshot,
      isSelf: p.is_owner,
      countsForHandicap: p.counts_for_handicap,
      scoreDifferential: p.score_differential,
    })) ?? []

  const activePlayer = players.find((p) => p.id === activePlayerId) ?? players[0] ?? null
  const apiHoles = activePlayerId ? holesByParticipant[activePlayerId] : undefined
  const holes: Hole[] = (apiHoles ?? []).map((h) => ({ holeNumber: h.hole_number, par: h.par, index: h.stroke_index }))
  const hole = holes.find((h) => h.holeNumber === currentHole) ?? null
  const totalHoles = 18

  const currentApiHole = apiHoles?.find((h) => h.hole_number === currentHole) ?? null
  const currentStat = currentApiHole ? apiHoleToStat(currentApiHole) : emptyStat()

  const showGir =
    currentStat.played &&
    currentStat.strokes !== null &&
    currentStat.putts !== null &&
    hole !== null &&
    currentStat.strokes - currentStat.putts <= hole.par - 2

  async function updateStat(patch: Partial<HoleStat>) {
    if (readOnly || !activePlayerId || !currentApiHole) return
    const merged: HoleStat = { ...currentStat, ...patch }
    const res = await fetch(`/rounds/${roundId}/participants/${activePlayerId}/holes/${currentHole}`, {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify(statToPatchBody(merged)),
    })
    if (!res.ok) return
    const updated: ApiHole = await res.json()
    setHolesByParticipant((prev) => ({
      ...prev,
      [activePlayerId]: (prev[activePlayerId] ?? []).map((h) => (h.hole_number === updated.hole_number ? updated : h)),
    }))
  }

  function holeIsPlayed(holeNumber: number) {
    return apiHoles?.find((h) => h.hole_number === holeNumber)?.played ?? false
  }

  function goPrev() {
    setCurrentHole((h) => Math.max(1, h - 1))
    setDetailsOpen(false)
  }
  function goNext() {
    setCurrentHole((h) => Math.min(totalHoles, h + 1))
    setDetailsOpen(false)
  }

  async function addGuest(name: string, gender: Gender, hcp: number | null) {
    const res = await fetch(`/rounds/${roundId}/participants`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify({ guest_name: name, gender: uiGenderToApi(gender), handicap_index: hcp }),
    })
    if (!res.ok) {
      setError("Klarte ikke å legge til spilleren. Sjekk at banen har en rating for valgt kjønn.")
      return
    }
    setShowAddGuest(false)
    const created: ApiParticipant = await res.json()
    await loadRound()
    setActivePlayerId(created.id)
  }

  async function removeGuest(id: string) {
    if (!confirm("Fjerne denne spilleren fra runden?")) return
    const res = await fetch(`/rounds/${roundId}/participants/${id}`, { method: "DELETE", credentials: "include" })
    if (!res.ok) return
    if (activePlayerId === id) setActivePlayerId(round?.participants.find((p) => p.is_owner)?.id ?? null)
    await loadRound()
  }

  async function finishRound() {
    if (!confirm("Fullføre runden? Du kan fortsatt se den, men ikke lenger endre registrerte hull.")) return
    setCompleting(true)
    try {
      const res = await fetch(`/rounds/${roundId}/complete`, { method: "POST", credentials: "include" })
      if (!res.ok) throw new Error()
      setRound(await res.json())
    } catch {
      setError("Klarte ikke å fullføre runden. Prøv igjen.")
    } finally {
      setCompleting(false)
    }
  }

  if (error) {
    return (
      <div className="flex min-h-[100dvh] flex-col items-center justify-center gap-4 bg-background px-5 text-center">
        <p className="text-base font-medium text-destructive">{error}</p>
        <Link href="/my-rounds" className="text-base font-semibold text-primary underline underline-offset-2">
          Tilbake til egne runder
        </Link>
      </div>
    )
  }

  if (!round || !activePlayer) {
    return (
      <div className="flex min-h-[100dvh] flex-col items-center justify-center bg-background">
        <div aria-hidden="true" className="size-10 animate-spin rounded-full border-4 border-primary/20 border-t-primary" />
      </div>
    )
  }

  return (
    <div className="flex min-h-[100dvh] flex-col bg-background">
      {/* Sticky header */}
      <header className="sticky top-0 z-20 border-b border-border bg-background/90 backdrop-blur">
        <div className="mx-auto flex w-full max-w-3xl items-center gap-3 px-5 py-4">
          <Link
            href="/my-rounds"
            aria-label="Tilbake til egne runder"
            className="flex size-11 shrink-0 items-center justify-center rounded-xl border border-border bg-card text-foreground transition-colors hover:bg-accent/50"
          >
            <ArrowLeft aria-hidden="true" className="size-5" />
          </Link>
          <div className="flex min-w-0 flex-col gap-0.5">
            <h1 className="flex items-center gap-2 truncate text-xl font-extrabold tracking-tight text-foreground">
              <MapPin aria-hidden="true" className="size-5 shrink-0 text-primary" />
              <span className="truncate">{round.course_name_snapshot}</span>
            </h1>
            <div className="flex flex-wrap items-center gap-x-4 gap-y-0.5 text-sm font-medium text-muted-foreground">
              <span className="flex items-center gap-1.5">
                <Flag aria-hidden="true" className="size-4 shrink-0" />
                {round.tee_name_snapshot}
              </span>
              <span className="flex items-center gap-1.5 tabular-nums">
                <CalendarDays aria-hidden="true" className="size-4 shrink-0" />
                {formatDate(round.played_at)}
              </span>
            </div>
          </div>
        </div>
      </header>

      <main className="mx-auto w-full max-w-3xl flex-1 px-5 py-6 sm:py-8">
        {error && (
          <p role="alert" className="mb-4 text-base font-medium text-destructive">
            {error}
          </p>
        )}

        {/* Completed summary banner */}
        {completed && <CompletedBanner players={players} />}

        {/* Participant tabs */}
        <PlayerTabs
          players={players}
          activePlayerId={activePlayer.id}
          onSelect={setActivePlayerId}
          onRemove={removeGuest}
          onAdd={() => setShowAddGuest((v) => !v)}
          addOpen={showAddGuest}
          readOnly={readOnly}
        />

        {showAddGuest && !readOnly && <AddGuestForm onAdd={addGuest} onCancel={() => setShowAddGuest(false)} />}

        {/* Hole navigation */}
        {holes.length === 0 ? (
          <div className="mt-5 flex justify-center py-8">
            <div aria-hidden="true" className="size-8 animate-spin rounded-full border-4 border-primary/20 border-t-primary" />
          </div>
        ) : (
          <>
            <HoleNav
              holes={holes}
              currentHole={currentHole}
              isPlayed={holeIsPlayed}
              onSelect={(n) => {
                setCurrentHole(n)
                setDetailsOpen(false)
              }}
            />

            {/* Current hole panel */}
            {hole && (
              <section className="mt-5 flex flex-col gap-6 rounded-3xl border border-border bg-card p-5 shadow-sm shadow-black/5 sm:p-6">
                <div className="flex items-start justify-between gap-3">
                  <h2 className="text-2xl font-extrabold tracking-tight text-foreground text-balance">
                    Hull {hole.holeNumber} · Par {hole.par} · Idx {hole.index}
                  </h2>
                  {showGir && (
                    <span className="shrink-0 rounded-full bg-primary px-3 py-1.5 text-sm font-bold text-primary-foreground">
                      GIR
                    </span>
                  )}
                </div>

                <PlayedToggle checked={currentStat.played} onChange={(v) => updateStat({ played: v })} readOnly={readOnly} />

                <NumberPicker
                  label="Slag"
                  value={currentStat.strokes}
                  directValues={[1, 2, 3, 4, 5, 6, 7, 8, 9]}
                  expandValues={[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]}
                  expandLabel="10+"
                  onChange={(v) => updateStat({ strokes: v, played: true })}
                  readOnly={readOnly}
                />

                <NumberPicker
                  label="Putter"
                  value={currentStat.putts}
                  directValues={[0, 1, 2, 3, 4, 5, 6]}
                  expandValues={[7, 8, 9, 10]}
                  expandLabel="7+"
                  onChange={(v) => updateStat({ putts: v })}
                  readOnly={readOnly}
                />

                <div className="rounded-2xl border border-border">
                  <button
                    type="button"
                    onClick={() => setDetailsOpen((v) => !v)}
                    aria-expanded={detailsOpen}
                    className="flex min-h-12 w-full items-center justify-between gap-2 px-4 py-3 text-left text-base font-semibold text-foreground"
                  >
                    Flere detaljer
                    <ChevronDown
                      aria-hidden="true"
                      className={cn("size-5 text-muted-foreground transition-transform", detailsOpen && "rotate-180")}
                    />
                  </button>
                  {detailsOpen && (
                    <div className="flex flex-col gap-6 border-t border-border p-4 sm:p-5">
                      <div className="flex flex-col gap-2">
                        <Label htmlFor="club" className="text-base font-semibold">
                          Kølle brukt ved utslaget
                        </Label>
                        <Input
                          id="club"
                          value={currentStat.club}
                          onChange={(e) => updateStat({ club: e.target.value })}
                          placeholder="F.eks. Driver, 3-jern"
                          disabled={readOnly}
                          className="h-12 rounded-2xl text-base"
                        />
                      </div>

                      {hole.par !== 3 && (
                        <ChoiceRow
                          label="Utslag"
                          options={[
                            { value: "left", label: "Venstre" },
                            { value: "fairway", label: "Fairway" },
                            { value: "right", label: "Høyre" },
                          ]}
                          value={currentStat.teeShot}
                          onChange={(v) => updateStat({ teeShot: v as TeeShot })}
                          readOnly={readOnly}
                        />
                      )}

                      <ChoiceRow
                        label="Innspill"
                        options={[
                          { value: "left", label: "Venstre" },
                          { value: "short", label: "Kort" },
                          { value: "hit", label: "Traff" },
                          { value: "long", label: "Langt" },
                          { value: "right", label: "Høyre" },
                        ]}
                        value={currentStat.approach}
                        onChange={(v) => updateStat({ approach: v as Approach })}
                        readOnly={readOnly}
                      />

                      <div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
                        <Stepper label="Chip" value={currentStat.chip} onChange={(v) => updateStat({ chip: v })} readOnly={readOnly} />
                        <Stepper label="Bunker" value={currentStat.bunker} onChange={(v) => updateStat({ bunker: v })} readOnly={readOnly} />
                        <Stepper label="Straffeslag" value={currentStat.penalty} onChange={(v) => updateStat({ penalty: v })} readOnly={readOnly} />
                      </div>

                      <div className="flex flex-col gap-2">
                        <Label htmlFor="putt-dist" className="text-base font-semibold">
                          Avstand første putt (m)
                        </Label>
                        <Input
                          id="putt-dist"
                          inputMode="decimal"
                          type="number"
                          min="0"
                          step="0.5"
                          value={currentStat.firstPuttDistance}
                          onChange={(e) => updateStat({ firstPuttDistance: e.target.value })}
                          placeholder="F.eks. 3.5"
                          disabled={readOnly}
                          className="h-12 rounded-2xl text-base"
                        />
                      </div>
                    </div>
                  )}
                </div>

                <div className="grid grid-cols-2 gap-3">
                  <Button type="button" variant="outline" onClick={goPrev} disabled={currentHole <= 1} className="h-14 rounded-2xl text-base font-bold">
                    <ChevronLeft aria-hidden="true" className="size-5" />
                    Forrige
                  </Button>
                  <Button type="button" variant="outline" onClick={goNext} disabled={currentHole >= totalHoles} className="h-14 rounded-2xl text-base font-bold">
                    Neste hull
                    <ChevronRight aria-hidden="true" className="size-5" />
                  </Button>
                </div>
              </section>
            )}
          </>
        )}

        {!completed && (
          <Button type="button" disabled={completing} onClick={finishRound} className="mt-6 h-14 w-full rounded-2xl text-base font-bold shadow-sm">
            <Check aria-hidden="true" className="size-5" />
            {completing ? "Fullfører…" : "Fullfør runde"}
          </Button>
        )}
      </main>
    </div>
  )
}

// --- Completed banner ------------------------------------------------------

function CompletedBanner({ players }: { players: Player[] }) {
  return (
    <div className="mb-6 flex flex-col gap-4 rounded-3xl border border-primary/30 bg-primary/10 p-5 sm:p-6">
      <div className="flex items-center gap-3">
        <div className="flex size-12 shrink-0 items-center justify-center rounded-2xl bg-primary text-primary-foreground shadow-sm">
          <Trophy aria-hidden="true" className="size-6" />
        </div>
        <div className="flex flex-col">
          <span className="text-xs font-semibold uppercase tracking-wide text-primary">Ferdigspilt</span>
          <h2 className="text-xl font-extrabold tracking-tight text-foreground">Runde fullført</h2>
        </div>
      </div>
      <ul className="flex flex-col divide-y divide-primary/20 overflow-hidden rounded-2xl border border-primary/20 bg-card">
        {players.map((player) => {
          const counts = player.countsForHandicap && player.scoreDifferential !== null
          return (
            <li key={player.id} className="flex items-center justify-between gap-3 px-4 py-3">
              <span className="text-base font-bold text-foreground">{player.name}</span>
              {counts ? (
                <span className="flex items-baseline gap-1.5">
                  <span className="text-xs font-medium text-muted-foreground">Differensial</span>
                  <span className="text-lg font-extrabold tabular-nums text-foreground">
                    {player.scoreDifferential!.toFixed(1).replace(".", ",")}
                  </span>
                </span>
              ) : (
                <span className="text-sm font-semibold text-muted-foreground">Telte ikke mot HCP</span>
              )}
            </li>
          )
        })}
      </ul>
    </div>
  )
}

// --- Player tabs -----------------------------------------------------------

function PlayerTabs({
  players,
  activePlayerId,
  onSelect,
  onRemove,
  onAdd,
  addOpen,
  readOnly,
}: {
  players: Player[]
  activePlayerId: string
  onSelect: (id: string) => void
  onRemove: (id: string) => void
  onAdd: () => void
  addOpen: boolean
  readOnly: boolean
}) {
  return (
    <div className="-mx-5 overflow-x-auto px-5">
      <div role="tablist" aria-label="Velg spiller" className="flex items-center gap-2">
        {players.map((player) => {
          const active = player.id === activePlayerId
          const canRemove = !player.isSelf && !readOnly
          return (
            <div
              key={player.id}
              className={cn(
                "flex min-h-12 shrink-0 items-center gap-1 rounded-2xl border pl-4 transition-colors",
                canRemove ? "pr-1.5" : "pr-4",
                active ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground hover:bg-accent/50",
              )}
            >
              <button type="button" role="tab" aria-selected={active} onClick={() => onSelect(player.id)} className="text-base font-bold">
                {player.name}
              </button>
              {canRemove && (
                <button
                  type="button"
                  onClick={() => onRemove(player.id)}
                  aria-label={`Fjern ${player.name}`}
                  className={cn(
                    "flex size-8 shrink-0 items-center justify-center rounded-xl transition-colors",
                    active ? "text-primary-foreground/80 hover:bg-primary-foreground/20" : "text-muted-foreground hover:bg-accent",
                  )}
                >
                  <X aria-hidden="true" className="size-4" />
                </button>
              )}
            </div>
          )
        })}
        {!readOnly && (
          <button
            type="button"
            onClick={onAdd}
            aria-label="Legg til gjestespiller"
            aria-expanded={addOpen}
            className="flex min-h-12 shrink-0 items-center gap-1.5 rounded-2xl border border-dashed border-border bg-card px-4 text-base font-semibold text-foreground transition-colors hover:bg-accent/50"
          >
            <Plus aria-hidden="true" className="size-5" />
            Gjest
          </button>
        )}
      </div>
    </div>
  )
}

// --- Add guest form --------------------------------------------------------

function AddGuestForm({
  onAdd,
  onCancel,
}: {
  onAdd: (name: string, gender: Gender, hcp: number | null) => void
  onCancel: () => void
}) {
  const [name, setName] = useState("")
  const [gender, setGender] = useState<Gender>("male")
  const [hcp, setHcp] = useState("")

  function handleSubmit(e: React.FormEvent) {
    e.preventDefault()
    const trimmed = name.trim()
    if (!trimmed) return
    const parsedHcp = hcp.trim() === "" ? null : Number(hcp.replace(",", "."))
    onAdd(trimmed, gender, parsedHcp !== null && !Number.isNaN(parsedHcp) ? parsedHcp : null)
  }

  return (
    <form onSubmit={handleSubmit} className="mt-3 flex flex-col gap-5 rounded-2xl border border-dashed border-border bg-card/50 p-4 sm:p-5">
      <div className="flex flex-col gap-2">
        <Label htmlFor="guest-name" className="text-base font-semibold">
          Navn
        </Label>
        <Input id="guest-name" autoFocus value={name} onChange={(e) => setName(e.target.value)} placeholder="Gjestens navn" className="h-12 rounded-2xl text-base" />
      </div>

      <ChoiceRow
        label="Kjønn"
        options={[
          { value: "male", label: "Mann" },
          { value: "female", label: "Kvinne" },
          { value: "other", label: "Annet" },
        ]}
        value={gender}
        onChange={(v) => setGender(v as Gender)}
        readOnly={false}
      />

      <div className="flex flex-col gap-2">
        <Label htmlFor="guest-hcp" className="text-base font-semibold">
          HCP <span className="font-normal text-muted-foreground">(valgfritt)</span>
        </Label>
        <Input
          id="guest-hcp"
          inputMode="decimal"
          type="number"
          step="0.1"
          value={hcp}
          onChange={(e) => setHcp(e.target.value)}
          placeholder="F.eks. 18"
          className="h-12 rounded-2xl text-base"
        />
      </div>

      <div className="flex gap-3">
        <Button type="submit" className="h-12 flex-1 rounded-2xl text-base font-bold">
          Legg til
        </Button>
        <Button type="button" variant="ghost" onClick={onCancel} className="h-12 rounded-2xl px-6 text-base font-semibold">
          Avbryt
        </Button>
      </div>
    </form>
  )
}

// --- Hole navigation -------------------------------------------------------

function HoleNav({
  holes,
  currentHole,
  isPlayed,
  onSelect,
}: {
  holes: Hole[]
  currentHole: number
  isPlayed: (holeNumber: number) => boolean
  onSelect: (holeNumber: number) => void
}) {
  return (
    <nav aria-label="Velg hull" className="-mx-5 mt-5 overflow-x-auto px-5">
      <div className="flex gap-2 pb-1">
        {holes.map((h) => {
          const active = h.holeNumber === currentHole
          const played = isPlayed(h.holeNumber)
          return (
            <button
              key={h.holeNumber}
              type="button"
              onClick={() => onSelect(h.holeNumber)}
              aria-current={active ? "true" : undefined}
              aria-label={`Hull ${h.holeNumber}${played ? ", spilt" : ""}`}
              className={cn(
                "flex size-12 shrink-0 items-center justify-center rounded-2xl border text-base font-bold tabular-nums transition-colors",
                active
                  ? "border-primary bg-primary text-primary-foreground"
                  : played
                    ? "border-primary/40 bg-primary/10 text-foreground hover:bg-primary/20"
                    : "border-border bg-card text-foreground hover:bg-accent/50",
              )}
            >
              {h.holeNumber}
            </button>
          )
        })}
      </div>
    </nav>
  )
}

// --- Played toggle ---------------------------------------------------------

function PlayedToggle({ checked, onChange, readOnly }: { checked: boolean; onChange: (value: boolean) => void; readOnly: boolean }) {
  return (
    <button
      type="button"
      role="checkbox"
      aria-checked={checked}
      disabled={readOnly}
      onClick={() => onChange(!checked)}
      className={cn(
        "flex min-h-12 items-center gap-3 rounded-2xl border px-4 py-3 text-left transition-colors disabled:opacity-100",
        checked ? "border-primary bg-primary/10" : "border-border bg-card hover:bg-accent/50",
      )}
    >
      <span
        aria-hidden="true"
        className={cn(
          "flex size-7 shrink-0 items-center justify-center rounded-lg border-2 transition-colors",
          checked ? "border-primary bg-primary text-primary-foreground" : "border-muted-foreground/50",
        )}
      >
        {checked && <Check className="size-5" strokeWidth={3} />}
      </span>
      <span className="text-base font-bold text-foreground">Hullet er spilt</span>
    </button>
  )
}

// --- Number picker ---------------------------------------------------------

function NumberPicker({
  label,
  value,
  directValues,
  expandValues,
  expandLabel,
  onChange,
  readOnly,
}: {
  label: string
  value: number | null
  directValues: number[]
  expandValues: number[]
  expandLabel: string
  onChange: (value: number) => void
  readOnly: boolean
}) {
  const valueInExpand = value !== null && expandValues.includes(value)
  const [expanded, setExpanded] = useState(false)
  const showExpanded = expanded || valueInExpand

  const visibleValues = showExpanded ? [...directValues, ...expandValues] : directValues

  return (
    <div className="flex flex-col gap-2">
      <span className="text-base font-semibold text-foreground">{label}</span>
      <div className="flex flex-wrap gap-2">
        {visibleValues.map((n) => {
          const selected = value === n
          return (
            <button
              key={n}
              type="button"
              disabled={readOnly}
              onClick={() => onChange(n)}
              aria-label={`${label} ${n}`}
              aria-pressed={selected}
              className={cn(
                "flex size-12 items-center justify-center rounded-2xl border text-lg font-bold tabular-nums transition-colors disabled:opacity-100",
                selected ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground hover:bg-accent/50",
                readOnly && !selected && "opacity-40",
              )}
            >
              {n}
            </button>
          )
        })}
        {!showExpanded && !readOnly && (
          <button
            type="button"
            onClick={() => setExpanded(true)}
            className="flex h-12 min-w-12 items-center justify-center rounded-2xl border border-dashed border-border bg-card px-3 text-base font-bold text-foreground transition-colors hover:bg-accent/50"
          >
            {expandLabel}
          </button>
        )}
      </div>
    </div>
  )
}

// --- Choice row (segmented buttons) ----------------------------------------

function ChoiceRow({
  label,
  options,
  value,
  onChange,
  readOnly,
}: {
  label: string
  options: { value: string; label: string }[]
  value: string | null
  onChange: (value: string) => void
  readOnly: boolean
}) {
  return (
    <div className="flex flex-col gap-2">
      <span className="text-base font-semibold text-foreground">{label}</span>
      <div className="flex flex-wrap gap-2">
        {options.map((opt) => {
          const selected = value === opt.value
          return (
            <button
              key={opt.value}
              type="button"
              disabled={readOnly}
              onClick={() => onChange(opt.value)}
              aria-pressed={selected}
              className={cn(
                "flex min-h-12 flex-1 items-center justify-center rounded-2xl border px-4 text-base font-bold transition-colors disabled:opacity-100",
                selected ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground hover:bg-accent/50",
                readOnly && !selected && "opacity-40",
              )}
            >
              {opt.label}
            </button>
          )
        })}
      </div>
    </div>
  )
}

// --- Stepper (+/-) ---------------------------------------------------------

function Stepper({ label, value, onChange, readOnly }: { label: string; value: number; onChange: (value: number) => void; readOnly: boolean }) {
  return (
    <div className="flex flex-col gap-2">
      <span className="text-base font-semibold text-foreground">{label}</span>
      <div className="flex items-center justify-between gap-2 rounded-2xl border border-border bg-card p-1.5">
        <button
          type="button"
          disabled={readOnly || value <= 0}
          onClick={() => onChange(Math.max(0, value - 1))}
          aria-label={`Færre ${label}`}
          className="flex size-11 shrink-0 items-center justify-center rounded-xl border border-border bg-background text-foreground transition-colors hover:bg-accent/50 disabled:opacity-40"
        >
          <Minus aria-hidden="true" className="size-5" />
        </button>
        <span className="min-w-8 text-center text-xl font-extrabold tabular-nums text-foreground">{value}</span>
        <button
          type="button"
          disabled={readOnly}
          onClick={() => onChange(value + 1)}
          aria-label={`Flere ${label}`}
          className="flex size-11 shrink-0 items-center justify-center rounded-xl border border-border bg-background text-foreground transition-colors hover:bg-accent/50 disabled:opacity-40"
        >
          <Plus aria-hidden="true" className="size-5" />
        </button>
      </div>
    </div>
  )
}
