"use client"

// Opprett en frittstående runde (ADR-033), som ÉN sammenhengende 5-stegs
// veiviser ("det store grepet", ADR-040 Beslutning A) -- erstatter det
// tidligere "ett langt skjema, så en helt annen skjerm etterpå"-mønsteret.
// Presentasjon fra V0 (zip 25), datalag skrevet om fra mock til ekte
// søk/opprettelse/innsending mot API-et.
//
// Rekkefølge: Bane & tid (inkl. bane-kilde-underskjermer) → Spilleform
// (inkl. HCP-innstillinger, ADR-040 Beslutning B) → Spillere → Lag/
// rekkefølge (kun to-sidede formater/Money Ball) → Deling. Selve runden
// (+ deltakere + sider/lineup) opprettes FØRST når hele veiviseren er
// fullført -- se submitWizard() nederst.

import type React from "react"
import { useEffect, useMemo, useState } from "react"
import { useRouter, useSearchParams } from "next/navigation"
import Link from "next/link"
import {
  ArrowLeft,
  Building2,
  Check,
  ChevronDown,
  Flag,
  Globe,
  Lock,
  MapPin,
  Pencil,
  Plus,
  Search,
  Sparkles,
  Trophy,
  Users,
  X,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
import { Wordmark } from "@/components/wordmark"
import { cn } from "@/lib/utils"

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

type ApiGender = "m" | "f"
// Tee bærer nå hvilket(e) kjønn den har rating for (ApiTeeOption sitt
// genders-felt) -- V0s egen Tee-type droppet dette, men uten det kan
// ikke utslagslisten filtreres riktig per spillers kjønn (samme sjekk
// backend selv håndhever ved innsending).
type Tee = { id: string; name: string; genders: ApiGender[] }
type Course = { id: string; name: string; tees: Tee[] }
type OfficialClub = { id: string; name: string; location: string; courses: Course[] }

// Hvilken bane-kilde+id som faktisk skal sendes til /rounds -- Course over
// bærer kun det UI-et trenger, ikke dette.
type CourseMeta =
  | { source: "teeoff"; facilitySlug: string; teeoffCourseId: number }
  | { source: "custom"; personalCourseId: string }

type StatLevel = "kun-slag" | "slag-putter" | "all"
type Gender = "mann" | "kvinne" | "annet"
type Visibility = "privat" | "venner" | "offentlig"

type Player = {
  id: string
  name: string
  isOwner?: boolean
  isGuest?: boolean
  // Satt når spilleren ble funnet via /people/search (ekte konto) -- avgjør
  // om vi sender user_id eller guest_name+gender ved innsending, og om
  // navn/kjønn/e-post kan redigeres i veiviseren (kun for gjester).
  userId?: string
  gender: Gender
  email?: string
  // "" = ikke valgt ennå -- faller tilbake til rundens eget utslag ved
  // innsending (samme mønster som ParticipantCreate.tee_name i appen for
  // øvrig).
  teeId: string
  hcp: string
  statLevel: StatLevel
  sideId?: string
  moneyBallSlot?: number
}

type Side = { id: string; label: string }

type S1Sub = "source" | "official-search" | "official-courses" | "own-search" | "own-create" | "fields"

// --- Formater ------------------------------------------------------------------

type FormatId =
  | "slagspill"
  | "stableford"
  | "match"
  | "skins"
  | "fourball"
  | "foursome"
  | "greensome"
  | "scramble2"
  | "scramble4"
  | "chapman"
  | "copenhagen"
  | "bbb"
  | "flag"
  | "shamble"
  | "moneyball"
  | "highlowhigh"

const FORMATS: { id: FormatId; label: string; desc: string }[] = [
  { id: "slagspill", label: "Slagspill", desc: "Vanlig individuell runde." },
  { id: "stableford", label: "Stableford", desc: "Poeng per hull i stedet for slag -- kan plukke opp ved 0 poeng." },
  { id: "match", label: "Match", desc: "To sider, hull mot hull." },
  { id: "skins", label: "Skins", desc: "Hvert hull er en egen premie -- minst 3 spillere." },
  { id: "fourball", label: "Fourball", desc: "To mot to, beste ball per side." },
  { id: "foursome", label: "Foursome", desc: "To mot to, én delt ball, annenhver slår." },
  { id: "greensome", label: "Greensome", desc: "To mot to, begge slår ut, velger så én ball." },
  { id: "scramble2", label: "Scramble (2)", desc: "To mot to, velger beste slag hver gang." },
  { id: "scramble4", label: "Scramble (4)", desc: "Fire mot fire, velger beste slag hver gang." },
  { id: "chapman", label: "Chapman", desc: "To mot to, bytter utslag, velger så én ball -- som foursome fra andre slag." },
  { id: "copenhagen", label: "Københavner", desc: "Nøyaktig 3 spillere -- 6 poeng deles per hull etter rangering." },
  { id: "bbb", label: "Bingo Bango Bongo", desc: "Poeng for først på green, nærmest hull, først i hull." },
  { id: "flag", label: "Flaggturnering", desc: "Fast slagbudsjett (par + HCP) -- flagget plantes der slagene tar slutt." },
  { id: "shamble", label: "Shamble", desc: "2-4 spillere, alle slår ut, velger beste -- spiller så egen ball inn." },
  { id: "moneyball", label: "Money Ball", desc: 'Fire spillere, én roterer som "money ball" per hull.' },
  { id: "highlowhigh", label: "High-low-high", desc: "To lag à to -- høy mot høy, lav mot lav, per hull." },
]

const TWO_SIDED = new Set<FormatId>([
  "match", "fourball", "foursome", "greensome", "scramble2", "scramble4", "chapman", "highlowhigh",
])
// Samme sett som app/handicap.py sin SIDE_IS_UNIT (+ chapman-aliaset) --
// avgjør om HCP-prosenten bygges som "combined" (delt enhet) eller
// "per_player" i allowance_override.strategy.
const SIDE_IS_UNIT_FORMAT = new Set<FormatId>(["foursome", "greensome", "scramble2", "scramble4", "chapman"])
// "Bruk course handicap-justering" er uten reell nytteverdi for de to
// enkleste, rene individuelle formatene (etterspurt av bruker 2026-08-02)
// -- skjules for disse, men holdes funksjonelt PÅ (aldri slettet/fjernet
// fra selve innsendingen, kun ikke eksponert som et valg i UI-et).
const ORDINARY_FORMATS = new Set<FormatId>(["slagspill", "stableford"])

function usesStep4(f: FormatId) {
  return TWO_SIDED.has(f) || f === "moneyball"
}

// Wizardens FormatId -> ekte round.play_format-verdi (RoundCreate).
const FORMAT_TO_API: Record<FormatId, string> = {
  slagspill: "stroke",
  stableford: "stableford",
  match: "match",
  skins: "skins",
  fourball: "fourball",
  foursome: "foursome",
  greensome: "greensome",
  scramble2: "scramble_2",
  scramble4: "scramble_4",
  chapman: "chapman",
  copenhagen: "copenhagen",
  bbb: "bbb",
  flag: "flag",
  shamble: "shamble",
  moneyball: "money_ball",
  highlowhigh: "high_low_high",
}
// Motsatt vei, brukt til å forhåndsutfylle format ved "legg til en flight
// til" (playFormat kommer fra URL som en ekte API-verdi).
const API_TO_FORMAT: Record<string, FormatId> = Object.fromEntries(
  Object.entries(FORMAT_TO_API).map(([k, v]) => [v, k as FormatId]),
) as Record<string, FormatId>

function apiStatLevel(s: StatLevel): "strokes_only" | "strokes_and_putts" | "full" {
  return s === "kun-slag" ? "strokes_only" : s === "slag-putter" ? "strokes_and_putts" : "full"
}
function apiGenderOf(g: Gender): "m" | "f" | "x" {
  return g === "mann" ? "m" : g === "kvinne" ? "f" : "x"
}
function uiGenderOf(g: ApiGender): Gender {
  return g === "m" ? "mann" : "kvinne"
}

// Samme "første ord = fornavn, resten = etternavn"-heuristikk som
// round-detail.tsx sin splitName() -- brukt til å forhåndsutfylle
// gjeste-navnefeltene fra søket når "Legg til uten konto (gjest)" trykkes.
function splitName(full: string): { first: string; last: string } {
  const trimmed = full.trim()
  const spaceIdx = trimmed.indexOf(" ")
  if (spaceIdx === -1) return { first: trimmed, last: "" }
  return { first: trimmed.slice(0, spaceIdx), last: trimmed.slice(spaceIdx + 1).trim() }
}

type KnownGuest = {
  guest_first_name: string
  guest_last_name: string | null
  gender: ApiGender | null
  handicap_index: number | null
}

const STAT_LEVELS: { id: StatLevel; label: string; desc: string }[] = [
  { id: "kun-slag", label: "Kun slag", desc: "Raskest — bare det som trengs for resultat og HCP" },
  { id: "slag-putter", label: "Slag og putter", desc: "Legger til putt-telling per hull" },
  { id: "all", label: "All statistikk", desc: "Kølle, retning, chip, bunker, straffeslag med mer" },
]

// Samme faste sett/rekkefølge som friends.py sin Category og
// friends.tsx/new-round.tsx (den forrige, ikke-veiviser-baserte
// implementasjonen) sin CATEGORY_OPTIONS -- egen, lokal kopi.
const CATEGORY_OPTIONS = [
  { code: "spouse", label: "Make" },
  { code: "close_family", label: "Nær familie" },
  { code: "extended_family", label: "Storfamilie" },
  { code: "close_friends", label: "Nære venner" },
  { code: "golf_friends", label: "Golfvenner" },
  { code: "colleagues", label: "Kollegaer" },
  { code: "business", label: "Forretningsforbindelser" },
  { code: "classmates", label: "Studiekamerater" },
  { code: "acquaintances", label: "Perifere bekjente" },
  { code: "other", label: "Ymse" },
] as const

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

type ApiTeeOption = { name: string; genders: ApiGender[] }
type ApiFacility = { slug: string; name: string; city: string | null; county: string | null }
type ApiNearbyFacility = ApiFacility & { distance_km: number }
type ApiOfficialCourseOption = {
  teeoff_course_id: number
  name: string
  is_main_course: boolean
  tees: ApiTeeOption[]
}
type ApiPersonalCourse = { id: string; name: string }
type ApiPersonalCourseDetail = { id: string; name: string; tees: ApiTeeOption[] }
type PersonMatch = {
  id: string
  first_name: string
  last_name: string
  avatar_url: string | null
  home_club: string | null
  handicap_index: number | null
}

function apiTeesToTees(apiTees: ApiTeeOption[]): Tee[] {
  return apiTees.map((t, i) => ({ id: `${t.name}-${i}`, name: t.name, genders: t.genders }))
}

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

let idSeq = 0
function newId(prefix: string) {
  idSeq += 1
  return `${prefix}-${idSeq}`
}

function initials(name: string) {
  const parts = name.trim().split(/\s+/)
  if (parts.length === 0 || parts[0] === "") return "?"
  if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase()
  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase()
}

function teeName(course: Course, teeId: string) {
  return course.tees.find((t) => t.id === teeId)?.name ?? "—"
}

function statLabel(level: StatLevel) {
  return STAT_LEVELS.find((s) => s.id === level)?.label ?? ""
}

function parseHcp(value: string): number | null {
  if (value.trim() === "") return null
  const n = Number(value.replace(",", "."))
  return Number.isNaN(n) ? null : n
}

// Dagens dato/klokkeslett som default ved oppsett av en ny runde (i lokal
// tidssone, ikke UTC -- en enkel toISOString().slice(0,10)/toTimeString()
// ville gitt feil dato/klokkeslett for brukere øst/vest for UTC nær
// midnatt).
function todayIso(): string {
  const now = new Date()
  const offset = now.getTimezoneOffset()
  const local = new Date(now.getTime() - offset * 60 * 1000)
  return local.toISOString().slice(0, 10)
}
function nowTimeString(): string {
  const now = new Date()
  return `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`
}

// --- Root --------------------------------------------------------------------

type Me = { gender: ApiGender | "x" | null; handicap_index: number | null; first_name: string | null; last_name: string | null; display_name: string }

export function NewRound() {
  const router = useRouter()
  const searchParams = useSearchParams()
  // Flere flighter i én "vanlig" runde (migrasjon 035) -- kommer fra
  // round-detail.tsx sin "Legg til en flight til"-handling.
  const flightGroupId = searchParams.get("flightGroupId")
  const prefillPlayedAt = searchParams.get("playedAt") ?? undefined
  const prefillStartHole = searchParams.get("startHole")
  const prefillHoles = searchParams.get("holes")
  const prefillPlayFormatApi = searchParams.get("playFormat")
  const isAdditionalFlight = Boolean(flightGroupId)

  const [loadingMe, setLoadingMe] = useState(true)
  const [ownGender, setOwnGender] = useState<ApiGender | null>(null)

  // Top-level step (1..5). Step 4 er hoppet over for enkelte formater.
  const [stepN, setStepN] = useState(1)

  // Step 1
  const [s1Sub, setS1Sub] = useState<S1Sub>("source")
  const [courseSource, setCourseSource] = useState<"official" | "own">("official")
  const [selectedClub, setSelectedClub] = useState<OfficialClub | null>(null)
  const [course, setCourse] = useState<Course | null>(null)
  const [courseMeta, setCourseMeta] = useState<CourseMeta | null>(null)
  const [roundName, setRoundName] = useState("")
  const [ownerTeeId, setOwnerTeeId] = useState("")
  const [date, setDate] = useState(() => prefillPlayedAt ?? todayIso())
  const [time, setTime] = useState(nowTimeString)
  const [startHole, setStartHole] = useState(prefillStartHole ?? "1")

  // Step 2
  const [statLevel, setStatLevel] = useState<StatLevel>("kun-slag")
  const [format, setFormat] = useState<FormatId>(
    (prefillPlayFormatApi && API_TO_FORMAT[prefillPlayFormatApi]) || "slagspill",
  )
  const [holes, setHoles] = useState<9 | 18>(prefillHoles === "9" ? 9 : 18)
  const [matchExcludeHcp, setMatchExcludeHcp] = useState(true)
  const [skinsScoring, setSkinsScoring] = useState<"netto" | "brutto">("netto")
  const [skinsTie, setSkinsTie] = useState<"roll" | "split">("roll")
  const [bbbBonus, setBbbBonus] = useState(false)
  const [shambleBest, setShambleBest] = useState(2)
  const [advOpen, setAdvOpen] = useState(false)
  const [useHcp, setUseHcp] = useState(true)
  const [useCourseHcp, setUseCourseHcp] = useState(true)
  const [useMatchplayHcp, setUseMatchplayHcp] = useState(true)
  const [hcpPercent, setHcpPercent] = useState("")

  // "Bruk course handicap-justering" er skjult for de ordinære formatene
  // (ORDINARY_FORMATS) -- siden brukeren da aldri ser bryteren, må den
  // funksjonelt holdes PÅ uansett hva den måtte ha stått i tidligere (f.eks.
  // slått av på et annet format før man byttet tilbake til Slagspill).
  useEffect(() => {
    if (ORDINARY_FORMATS.has(format)) setUseCourseHcp(true)
  }, [format])

  // Step 3
  const [players, setPlayers] = useState<Player[]>([])

  // Step 4
  const [sides, setSides] = useState<Side[]>([])

  // Step 5
  const [visibility, setVisibility] = useState<Visibility>("privat")
  const [categories, setCategories] = useState<Set<string>>(new Set(CATEGORY_OPTIONS.map((c) => c.code)))

  const [submitting, setSubmitting] = useState(false)
  const [submitError, setSubmitError] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false
    async function loadMe() {
      try {
        const res = await fetch("/auth/me", { credentials: "include" })
        if (res.status === 401) {
          router.replace("/")
          return
        }
        const data: Me = await res.json()
        if (cancelled) return
        if (data.gender === "m" || data.gender === "f") setOwnGender(data.gender)
        const ownerName = data.first_name ? `${data.first_name} ${data.last_name ?? ""}`.trim() : data.display_name
        setPlayers([
          {
            id: "owner",
            name: ownerName,
            isOwner: true,
            gender: data.gender === "f" ? "kvinne" : "mann",
            teeId: "",
            hcp: data.handicap_index !== null ? String(data.handicap_index).replace(".", ",") : "",
            statLevel: "kun-slag",
          },
        ])
      } finally {
        if (!cancelled) setLoadingMe(false)
      }
    }
    void loadMe()
    return () => {
      cancelled = true
    }
  }, [router])

  const showStep4 = usesStep4(format)
  const stepList = useMemo(() => {
    const base = [
      { n: 1, title: "Bane & tid" },
      { n: 2, title: "Spilleform" },
      { n: 3, title: "Spillere" },
    ]
    if (showStep4) base.push({ n: 4, title: "Lag og rekkefølge" })
    base.push({ n: 5, title: "Deling" })
    return base
  }, [showStep4])

  const currentIndex = Math.max(0, stepList.findIndex((s) => s.n === stepN))

  function chooseCourse(c: Course, meta: CourseMeta, source: "official" | "own") {
    setCourse(c)
    setCourseMeta(meta)
    setCourseSource(source)
    const compatible = ownGender ? c.tees.filter((t) => t.genders.includes(ownGender)) : c.tees
    const firstTee = compatible[0]?.id ?? ""
    setOwnerTeeId(firstTee)
    setPlayers((prev) => prev.map((p) => (p.isOwner ? { ...p, teeId: firstTee } : p)))
    setS1Sub("fields")
  }

  const step1Valid = Boolean(course && ownerTeeId && date)
  const canNext = stepN === 1 ? step1Valid && s1Sub === "fields" : true

  const isFinalStep = stepList[stepList.length - 1].n === stepN
  const showNextButton = stepN !== 1 || s1Sub === "fields"

  async function handleNext() {
    if (isFinalStep) {
      await submitWizard()
      return
    }
    const next = stepList[currentIndex + 1]
    if (next) setStepN(next.n)
  }

  function handleBack() {
    if (stepN === 1) {
      if (s1Sub === "source") {
        router.push("/my-rounds")
      } else if (s1Sub === "official-search") {
        setS1Sub("source")
      } else if (s1Sub === "official-courses") {
        setS1Sub("official-search")
      } else if (s1Sub === "own-search") {
        setS1Sub("source")
      } else if (s1Sub === "own-create") {
        setS1Sub("own-search")
      } else if (s1Sub === "fields") {
        setS1Sub(courseSource === "official" ? "official-courses" : "own-search")
      }
      return
    }
    const prev = stepList[currentIndex - 1]
    if (prev) setStepN(prev.n)
  }

  // --- Innsending (ADR-040): runden finnes ikke før HELE veiviseren er
  // fullført -- oppretter runden (eier), deretter sidene (om to-sidet),
  // deretter hver medspiller (med round_side_id satt direkte der siden
  // allerede finnes), deretter eierens egen side-tildeling og eventuell
  // Money Ball-rekkefølge (begge krever PATCH, siden ParticipantCreate
  // ikke støtter lineup_order og eieren allerede er opprettet FØR sidene).
  async function submitWizard() {
    if (!course || !courseMeta || !ownerTeeId) return
    setSubmitting(true)
    setSubmitError(null)
    try {
      const startedAt = time ? new Date(`${date}T${time}`).toISOString() : null
      const apiFormat = FORMAT_TO_API[format]
      const allowanceOverride = buildAllowanceOverride()

      const shared = {
        tee_name: teeName(course, ownerTeeId),
        name: roundName.trim() || null,
        played_at: date,
        started_at: startedAt,
        start_hole: Number(startHole),
        holes_planned: holes,
        stat_level: apiStatLevel(players.find((p) => p.isOwner)?.statLevel ?? "kun-slag"),
        play_format: apiFormat,
        exclude_owner_from_handicap: format === "match" ? matchExcludeHcp : false,
        skins_scoring: format === "skins" ? (skinsScoring === "netto" ? "net" : "gross") : null,
        skins_tie_handling: format === "skins" ? (skinsTie === "roll" ? "carry" : "split") : null,
        bbb_sweep_bonus_enabled: format === "bbb" ? bbbBonus : false,
        shamble_best_n: format === "shamble" ? shambleBest : null,
        visibility_mode: visibility === "privat" ? "private" : visibility === "venner" ? "friends" : "public",
        visible_categories: visibility === "venner" ? Array.from(categories) : [],
        flight_group_id: flightGroupId || undefined,
        allowance_override: allowanceOverride,
      }
      const body =
        courseMeta.source === "teeoff"
          ? {
              course_source: "teeoff",
              teeoff_facility_slug: courseMeta.facilitySlug,
              teeoff_course_id: courseMeta.teeoffCourseId,
              ...shared,
            }
          : { course_source: "custom", personal_course_id: courseMeta.personalCourseId, ...shared }

      const roundRes = await fetch("/rounds", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        credentials: "include",
        body: JSON.stringify(body),
      })
      if (!roundRes.ok) throw new Error(`create round: ${roundRes.status}`)
      const round: { id: string; participants: { id: string; is_owner: boolean }[] } = await roundRes.json()
      const roundId = round.id
      const ownerParticipantId = round.participants.find((p) => p.is_owner)?.id ?? round.participants[0]?.id

      // Sider (kun to-sidede formater) -- opprettet FØR medspillerne slik
      // at round_side_id kan sendes direkte på hver ParticipantCreate.
      const sideIdMap = new Map<string, string>()
      if (TWO_SIDED.has(format)) {
        for (const side of sides) {
          const res = await fetch(`/rounds/${roundId}/sides`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            credentials: "include",
            body: JSON.stringify({ label: side.label || null }),
          })
          if (!res.ok) throw new Error(`create side: ${res.status}`)
          const created: { id: string } = await res.json()
          sideIdMap.set(side.id, created.id)
        }
      }

      // Medspillere (alle unntatt eieren, som allerede finnes).
      const participantIdMap = new Map<string, string>([["owner", ownerParticipantId]])
      for (const p of players) {
        if (p.isOwner) continue
        const teeSelected = p.teeId ? teeName(course, p.teeId) : undefined
        const realSideId = p.sideId ? sideIdMap.get(p.sideId) : undefined
        const participantBody: Record<string, unknown> = {
          stat_level: apiStatLevel(p.statLevel),
          ...(teeSelected ? { tee_name: teeSelected } : {}),
          ...(realSideId ? { round_side_id: realSideId } : {}),
        }
        if (p.userId) {
          participantBody.user_id = p.userId
        } else {
          // Samme "første ord = fornavn, resten = etternavn"-heuristikk som
          // round-detail.tsx sin splitName()/migrasjon 052 sin backfill
          // (2026-08-03 -- ParticipantCreate godtar ikke lenger guest_name
          // direkte, kun guest_first_name/guest_last_name).
          const trimmedName = p.name.trim()
          const spaceIdx = trimmedName.indexOf(" ")
          participantBody.guest_first_name = spaceIdx === -1 ? trimmedName : trimmedName.slice(0, spaceIdx)
          participantBody.guest_last_name = spaceIdx === -1 ? null : trimmedName.slice(spaceIdx + 1).trim()
          participantBody.gender = apiGenderOf(p.gender)
          participantBody.handicap_index = parseHcp(p.hcp)
          if (p.email && p.email.trim()) participantBody.guest_email = p.email.trim()
        }
        const res = await fetch(`/rounds/${roundId}/participants`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          credentials: "include",
          body: JSON.stringify(participantBody),
        })
        if (!res.ok) {
          const errBody = await res.json().catch(() => null)
          throw new Error(errBody?.detail?.message ?? `add participant: ${res.status}`)
        }
        const created: { id: string } = await res.json()
        participantIdMap.set(p.id, created.id)
      }

      // Eierens egen side-tildeling (PATCH -- eieren finnes allerede FØR
      // sidene ble opprettet over).
      if (TWO_SIDED.has(format)) {
        const owner = players.find((p) => p.isOwner)
        const realSideId = owner?.sideId ? sideIdMap.get(owner.sideId) : undefined
        if (realSideId) {
          await fetch(`/rounds/${roundId}/participants/${ownerParticipantId}`, {
            method: "PATCH",
            headers: { "Content-Type": "application/json" },
            credentials: "include",
            body: JSON.stringify({ round_side_id: realSideId }),
          })
        }
      }

      // Money Ball-rekkefølge (PATCH -- ikke støttet på selve opprettelsen).
      if (format === "moneyball") {
        for (const p of players) {
          if (p.moneyBallSlot === undefined) continue
          const realId = participantIdMap.get(p.id)
          if (!realId) continue
          await fetch(`/rounds/${roundId}/participants/${realId}`, {
            method: "PATCH",
            headers: { "Content-Type": "application/json" },
            credentials: "include",
            body: JSON.stringify({ lineup_order: p.moneyBallSlot }),
          })
        }
      }

      router.replace(`/my-rounds/${roundId}`)
    } catch (err) {
      setSubmitError(
        err instanceof Error && err.message && !err.message.startsWith("create round:") && !err.message.startsWith("create side:")
          ? err.message
          : "Klarte ikke å opprette runden. Prøv igjen.",
      )
      setSubmitting(false)
    }
  }

  // ADR-040 Beslutning B -- HCP-prosenten er alltid justerbar, og
  // Match-HCP kan slås av/på for to-sidede formater. Samme mønster som
  // tournament-program.tsx sin CreateSessionCard.buildAllowanceOverride.
  function buildAllowanceOverride(): Record<string, unknown> | null {
    const percentValue = hcpPercent.trim() === "" ? undefined : Number(hcpPercent.replace(",", "."))
    const hasPercent = percentValue !== undefined && !Number.isNaN(percentValue)
    const twoSided = TWO_SIDED.has(format)
    const matchplayIsDefault = !twoSided || useMatchplayHcp

    if (useHcp && useCourseHcp && matchplayIsDefault && !hasPercent) return null

    const override: Record<string, unknown> = { use_handicap: useHcp, use_course_handicap: useCourseHcp }
    if (twoSided) override.use_matchplay_handicap = useMatchplayHcp
    if (hasPercent) {
      const fraction = percentValue! / 100
      override.strategy = SIDE_IS_UNIT_FORMAT.has(format)
        ? { type: "combined", percentage: fraction }
        : { type: "per_player", percentage: fraction }
    }
    return override
  }

  if (loadingMe) {
    return (
      <div className="flex min-h-[100dvh] 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>
    )
  }

  if (!ownGender) {
    return (
      <div className="flex min-h-[100dvh] flex-col items-center justify-center gap-4 bg-background px-5 text-center">
        <p className="max-w-prose text-lg text-muted-foreground">
          Profilen din mangler registrert kjønn, som trengs for å beregne banehandicap riktig.{" "}
          <Link href="/account" className="font-semibold text-primary underline underline-offset-2">
            Gå til kontoinnstillinger
          </Link>
          .
        </p>
      </div>
    )
  }

  return (
    <div className="flex min-h-[100dvh] flex-col bg-background">
      <header className="sticky top-0 z-20 border-b border-border bg-background">
        <div className="mx-auto flex w-full max-w-3xl items-center justify-between gap-4 px-5 py-3.5">
          <button
            type="button"
            onClick={handleBack}
            className="inline-flex min-h-[44px] items-center gap-2 rounded-xl px-2 py-2 text-base font-semibold text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
          >
            <ArrowLeft aria-hidden="true" className="size-5" />
            Tilbake
          </button>
          <Wordmark compact />
        </div>
        <div className="mx-auto w-full max-w-3xl px-5 pb-4">
          <StepIndicator steps={stepList} currentIndex={currentIndex} />
        </div>
      </header>

      {/* pb-32 er reservert klaring for den faste Tilbake/Neste-linjen under --
          ALDRI en py- eller sm:py-klasse her (Tailwind emitterer sm-varianter
          i en egen media-blokk etter grunnklassene, så en sm:py-8 ville slått
          padding-bottom tilbake til 32px på alle skjermer 640px og bredere,
          uansett rekkefølge i selve className-strengen -- reell bug funnet
          2026-08-02, se CHANGELOG.md). En pt-klasse er trygt, den påvirker
          aldri padding-bottom. */}
      <main className="mx-auto w-full max-w-3xl flex-1 px-5 pt-6 pb-32 sm:pt-8">
        {isAdditionalFlight && stepN === 1 && s1Sub !== "fields" && (
          <p className="mb-6 rounded-2xl border border-dashed border-primary/40 bg-primary/5 px-4 py-3 text-sm font-semibold text-foreground">
            Du setter opp enda en flight i samme oppsett -- dato, starthull, hullantall og spilleform er
            forhåndsutfylt fra den første flighten, men kan endres.
          </p>
        )}

        {submitError && (
          <p role="alert" className="mb-6 rounded-2xl border border-destructive/40 bg-destructive/10 px-4 py-3 text-base font-semibold text-destructive">
            {submitError}
          </p>
        )}

        {stepN === 1 && (
          <Step1
            s1Sub={s1Sub}
            setS1Sub={setS1Sub}
            selectedClub={selectedClub}
            setSelectedClub={setSelectedClub}
            setCourseSource={setCourseSource}
            course={course}
            chooseCourse={chooseCourse}
            ownGender={ownGender}
            roundName={roundName}
            setRoundName={setRoundName}
            ownerTeeId={ownerTeeId}
            setOwnerTeeId={(id) => {
              setOwnerTeeId(id)
              setPlayers((prev) => prev.map((p) => (p.isOwner ? { ...p, teeId: id } : p)))
            }}
            date={date}
            setDate={setDate}
            time={time}
            setTime={setTime}
            startHole={startHole}
            setStartHole={setStartHole}
          />
        )}

        {stepN === 2 && (
          <Step2
            statLevel={statLevel}
            setStatLevel={(v) => {
              setStatLevel(v)
              setPlayers((prev) => prev.map((p) => (p.isOwner ? { ...p, statLevel: v } : p)))
            }}
            format={format}
            setFormat={setFormat}
            holes={holes}
            setHoles={setHoles}
            matchExcludeHcp={matchExcludeHcp}
            setMatchExcludeHcp={setMatchExcludeHcp}
            skinsScoring={skinsScoring}
            setSkinsScoring={setSkinsScoring}
            skinsTie={skinsTie}
            setSkinsTie={setSkinsTie}
            bbbBonus={bbbBonus}
            setBbbBonus={setBbbBonus}
            shambleBest={shambleBest}
            setShambleBest={setShambleBest}
            advOpen={advOpen}
            setAdvOpen={setAdvOpen}
            useHcp={useHcp}
            setUseHcp={setUseHcp}
            useCourseHcp={useCourseHcp}
            setUseCourseHcp={setUseCourseHcp}
            useMatchplayHcp={useMatchplayHcp}
            setUseMatchplayHcp={setUseMatchplayHcp}
            hcpPercent={hcpPercent}
            setHcpPercent={setHcpPercent}
          />
        )}

        {stepN === 3 && course && (
          <Step3 course={course} players={players} setPlayers={setPlayers} roundStatLevel={statLevel} />
        )}

        {stepN === 4 && (
          <Step4 format={format} players={players} setPlayers={setPlayers} sides={sides} setSides={setSides} />
        )}

        {stepN === 5 && (
          <Step5 visibility={visibility} setVisibility={setVisibility} categories={categories} setCategories={setCategories} />
        )}
      </main>

      {/* Sticky wizard nav */}
      <div className="fixed inset-x-0 bottom-0 z-20 border-t border-border bg-background">
        <div className="mx-auto flex w-full max-w-3xl items-center gap-3 px-5 py-4">
          <Button
            type="button"
            variant="outline"
            size="lg"
            onClick={handleBack}
            disabled={submitting}
            className="h-14 rounded-2xl px-6 text-base font-semibold"
          >
            Tilbake
          </Button>
          {showNextButton && (
            <Button
              type="button"
              size="lg"
              disabled={!canNext || submitting}
              onClick={handleNext}
              className={cn("h-14 flex-1 rounded-2xl text-lg shadow-sm", isFinalStep ? "font-extrabold" : "font-bold")}
            >
              {isFinalStep ? (submitting ? "Oppretter…" : "Start runden") : "Neste"}
            </Button>
          )}
        </div>
      </div>
    </div>
  )
}

// --- Step indicator --------------------------------------------------------

function StepIndicator({ steps, currentIndex }: { steps: { n: number; title: string }[]; currentIndex: number }) {
  return (
    <div>
      <ol className="flex items-center gap-1.5" aria-label="Fremdrift">
        {steps.map((s, i) => {
          const done = i < currentIndex
          const current = i === currentIndex
          return (
            <li key={s.n} className="flex flex-1 items-center gap-1.5">
              <span
                className={cn(
                  "flex size-8 shrink-0 items-center justify-center rounded-full text-sm font-bold",
                  current && "bg-primary text-primary-foreground",
                  done && "bg-primary/20 text-primary",
                  !current && !done && "bg-muted text-muted-foreground",
                )}
                aria-current={current ? "step" : undefined}
              >
                {done ? <Check aria-hidden="true" className="size-4" /> : i + 1}
              </span>
              {i < steps.length - 1 && (
                <span className={cn("h-1 flex-1 rounded-full", i < currentIndex ? "bg-primary/40" : "bg-muted")} />
              )}
            </li>
          )
        })}
      </ol>
      <p className="mt-2.5 text-sm font-semibold text-muted-foreground">
        Steg {currentIndex + 1} av {steps.length}
        <span className="mx-1.5 text-border">·</span>
        <span className="text-foreground">{steps[currentIndex].title}</span>
      </p>
    </div>
  )
}

// --- Delt UI -----------------------------------------------------------------

function StepHeader({ title, description }: { title: string; description?: string }) {
  return (
    <div className="flex flex-col gap-2">
      <h1 className="text-3xl font-extrabold tracking-tight text-foreground text-balance">{title}</h1>
      {description && <p className="max-w-prose text-lg leading-relaxed text-muted-foreground text-pretty">{description}</p>}
    </div>
  )
}

function SectionTitle({ title, hint }: { title: string; hint?: string }) {
  return (
    <div className="flex flex-col gap-1">
      <h2 className="text-xl font-bold text-foreground">{title}</h2>
      {hint && <p className="text-base leading-relaxed text-muted-foreground">{hint}</p>}
    </div>
  )
}

function Pill({ selected, onClick, children }: { selected: boolean; onClick: () => void; children: React.ReactNode }) {
  return (
    <button
      type="button"
      onClick={onClick}
      aria-pressed={selected}
      className={cn(
        "inline-flex min-h-[44px] items-center justify-center rounded-full border px-5 text-base font-bold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
        selected ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground hover:border-primary/50 hover:bg-accent/40",
      )}
    >
      {children}
    </button>
  )
}

function SegChoice<T extends string>({
  label,
  value,
  options,
  onChange,
}: {
  label: string
  value: T
  options: { value: T; label: string }[]
  onChange: (v: T) => void
}) {
  return (
    <div role="group" aria-label={label} className="grid gap-2" style={{ gridTemplateColumns: `repeat(${options.length}, minmax(0, 1fr))` }}>
      {options.map((o) => {
        const selected = o.value === value
        return (
          <button
            key={o.value}
            type="button"
            onClick={() => onChange(o.value)}
            aria-pressed={selected}
            className={cn(
              "min-h-[48px] rounded-2xl border px-3 text-base font-bold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
              selected ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground hover:border-primary/50 hover:bg-accent/40",
            )}
          >
            {o.label}
          </button>
        )
      })}
    </div>
  )
}

function ChoiceCard({
  selected,
  onClick,
  title,
  description,
  icon,
}: {
  selected: boolean
  onClick: () => void
  title: string
  description: string
  icon?: React.ReactNode
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      aria-pressed={selected}
      className={cn(
        "flex min-h-[44px] w-full flex-col items-start gap-2 rounded-2xl border-2 p-5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
        selected ? "border-primary bg-primary/5" : "border-border bg-card hover:border-primary/40 hover:bg-accent/40",
      )}
    >
      <div className="flex w-full items-center gap-3">
        {icon && (
          <span className={cn("flex size-11 shrink-0 items-center justify-center rounded-xl", selected ? "bg-primary/15 text-primary" : "bg-muted text-muted-foreground")}>
            {icon}
          </span>
        )}
        <span className="flex-1 text-lg font-bold text-foreground">{title}</span>
        <span className={cn("flex size-6 shrink-0 items-center justify-center rounded-full border-2", selected ? "border-primary bg-primary text-primary-foreground" : "border-border")} aria-hidden="true">
          {selected && <Check className="size-4" />}
        </span>
      </div>
      <p className="text-base leading-relaxed text-muted-foreground text-pretty">{description}</p>
    </button>
  )
}

function CheckRow({ checked, onChange, label, helper }: { checked: boolean; onChange: (v: boolean) => void; label: string; helper?: string }) {
  return (
    <div className="flex flex-col gap-1.5">
      <button
        type="button"
        role="checkbox"
        aria-checked={checked}
        onClick={() => onChange(!checked)}
        className="flex min-h-[44px] items-center gap-3 rounded-2xl border border-border bg-card p-4 text-left transition-colors hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
      >
        <span className={cn("flex size-6 shrink-0 items-center justify-center rounded-md border-2", checked ? "border-primary bg-primary text-primary-foreground" : "border-border")} aria-hidden="true">
          {checked && <Check className="size-4" />}
        </span>
        <span className="text-base font-semibold text-foreground">{label}</span>
      </button>
      {helper && <p className="px-1 text-sm leading-relaxed text-muted-foreground">{helper}</p>}
    </div>
  )
}

function NumberButtons({ values, value, onChange, label }: { values: number[]; value: number | undefined; onChange: (v: number) => void; label: string }) {
  return (
    <div role="group" aria-label={label} className="flex flex-wrap gap-2">
      {values.map((v) => {
        const selected = v === value
        return (
          <button
            key={v}
            type="button"
            onClick={() => onChange(v)}
            aria-pressed={selected}
            className={cn(
              "flex size-12 items-center justify-center rounded-2xl border text-lg font-bold tabular-nums transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
              selected ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground hover:border-primary/50 hover:bg-accent/40",
            )}
          >
            {v}
          </button>
        )
      })}
    </div>
  )
}

function Avatar({ name }: { name: string }) {
  return <span className="flex size-10 shrink-0 items-center justify-center rounded-full bg-muted text-sm font-bold text-muted-foreground">{initials(name)}</span>
}

function HelperNote({ children }: { children: React.ReactNode }) {
  return <p className="rounded-2xl border border-border bg-muted/50 px-4 py-3 text-base leading-relaxed text-muted-foreground text-pretty">{children}</p>
}

// --- Step 1: Bane & tid ------------------------------------------------------

function Step1(props: {
  s1Sub: S1Sub
  setS1Sub: (s: S1Sub) => void
  selectedClub: OfficialClub | null
  setSelectedClub: (c: OfficialClub | null) => void
  setCourseSource: (s: "official" | "own") => void
  course: Course | null
  chooseCourse: (c: Course, meta: CourseMeta, source: "official" | "own") => void
  ownGender: ApiGender
  roundName: string
  setRoundName: (v: string) => void
  ownerTeeId: string
  setOwnerTeeId: (v: string) => void
  date: string
  setDate: (v: string) => void
  time: string
  setTime: (v: string) => void
  startHole: string
  setStartHole: (v: string) => void
}) {
  const { s1Sub, setS1Sub, selectedClub, setSelectedClub, setCourseSource, course, chooseCourse, ownGender } = props
  const [resolvingClub, setResolvingClub] = useState(false)

  if (s1Sub === "source") {
    return (
      <div className="flex flex-col gap-8">
        <StepHeader title="Bane & tid" description="Velg hvor banen kommer fra for å komme i gang." />
        <div className="grid gap-4 sm:grid-cols-2">
          <SourceCard
            icon={<Trophy aria-hidden="true" className="size-7 text-muted-foreground" />}
            title="Offisiell bane"
            description="Søk opp banen i det offisielle baneregisteret."
            onClick={() => {
              setCourseSource("official")
              setS1Sub("official-search")
            }}
          />
          <SourceCard
            icon={<Pencil aria-hidden="true" className="size-7 text-muted-foreground" />}
            title="Egen bane"
            description="Søk opp eller opprett en personlig bane."
            onClick={() => {
              setCourseSource("own")
              setS1Sub("own-search")
            }}
          />
        </div>
      </div>
    )
  }

  if (s1Sub === "official-search") {
    return (
      <OfficialSearch
        resolving={resolvingClub}
        onSelectClub={async (facility) => {
          setResolvingClub(true)
          try {
            const res = await fetch(`/rounds/official-search/${facility.slug}`, { credentials: "include" })
            if (!res.ok) throw new Error(`facility detail: ${res.status}`)
            const detail: { name: string; courses: ApiOfficialCourseOption[] } = await res.json()
            const club: OfficialClub = {
              id: facility.slug,
              name: detail.name,
              location: [facility.city, facility.county].filter(Boolean).join(", "),
              courses: detail.courses.map((c) => ({ id: String(c.teeoff_course_id), name: c.name, tees: apiTeesToTees(c.tees) })),
            }
            setSelectedClub(club)
            setS1Sub("official-courses")
          } catch {
            setSelectedClub(null)
          } finally {
            setResolvingClub(false)
          }
        }}
      />
    )
  }

  if (s1Sub === "official-courses" && selectedClub) {
    return (
      <CourseList
        title={selectedClub.name}
        description="Velg hvilken bane du skal spille."
        courses={selectedClub.courses}
        onSelect={(c) => chooseCourse(c, { source: "teeoff", facilitySlug: selectedClub.id, teeoffCourseId: Number(c.id) }, "official")}
      />
    )
  }

  if (s1Sub === "own-search") {
    return (
      <OwnSearch
        onSelect={async (picked) => {
          const res = await fetch(`/personal-courses/${picked.id}`, { credentials: "include" })
          if (!res.ok) return
          const detail: ApiPersonalCourseDetail = await res.json()
          chooseCourse(
            { id: detail.id, name: detail.name, tees: apiTeesToTees(detail.tees) },
            { source: "custom", personalCourseId: detail.id },
            "own",
          )
        }}
        onCreate={() => setS1Sub("own-create")}
      />
    )
  }

  if (s1Sub === "own-create") {
    return (
      <OwnCreateStep
        onCreated={(created) => chooseCourse(created, { source: "custom", personalCourseId: created.id }, "own")}
        onCancel={() => setS1Sub("own-search")}
      />
    )
  }

  // s1Sub === "fields"
  if (course) {
    return <Step1Fields {...props} course={course} ownGender={ownGender} />
  }
  return null
}

function SourceCard({ icon, title, description, onClick }: { icon: React.ReactNode; title: string; description: string; onClick: () => void }) {
  return (
    <button
      type="button"
      onClick={onClick}
      className="flex min-h-[44px] flex-col items-start gap-4 rounded-3xl border border-border bg-card p-6 text-left shadow-md shadow-black/8 transition-colors hover:border-primary/50 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
    >
      <div className="flex size-14 items-center justify-center rounded-2xl bg-muted">{icon}</div>
      <div className="flex flex-col gap-1.5">
        <h2 className="text-xl font-bold text-foreground">{title}</h2>
        <p className="text-base leading-relaxed text-muted-foreground text-pretty">{description}</p>
      </div>
    </button>
  )
}

function NearbyClubs({ onSelectClub, resolving }: { onSelectClub: (f: ApiFacility) => void; resolving: boolean }) {
  const [nearby, setNearby] = useState<ApiNearbyFacility[] | null>(null)

  useEffect(() => {
    if (!("geolocation" in navigator)) return
    let cancelled = false
    navigator.geolocation.getCurrentPosition(
      (position) => {
        if (cancelled) return
        const { latitude, longitude } = position.coords
        fetch(`/rounds/official-search/nearby?lat=${latitude}&lng=${longitude}&limit=5`, { credentials: "include" })
          .then((res) => (res.ok ? res.json() : null))
          .then((data: ApiNearbyFacility[] | null) => {
            if (!cancelled && data) setNearby(data)
          })
          .catch(() => {})
      },
      () => {},
      { timeout: 8000 },
    )
    return () => {
      cancelled = true
    }
  }, [])

  if (!nearby || nearby.length === 0) return null

  return (
    <div className="flex flex-col gap-3">
      <SectionTitle title="Nærmest deg" />
      <ul className="flex flex-col gap-3">
        {nearby.map((facility) => (
          <li key={facility.slug}>
            <ClubRow
              name={facility.name}
              location={[facility.city, facility.county].filter(Boolean).join(", ")}
              distance={facility.distance_km < 1 ? `${Math.round(facility.distance_km * 1000)} m` : `${facility.distance_km.toFixed(0)} km`}
              disabled={resolving}
              onClick={() => onSelectClub(facility)}
            />
          </li>
        ))}
      </ul>
    </div>
  )
}

function OfficialSearch({ onSelectClub, resolving }: { onSelectClub: (facility: ApiFacility) => void; resolving: boolean }) {
  const [query, setQuery] = useState("")
  const [results, setResults] = useState<ApiFacility[] | null>(null)
  const [searching, setSearching] = useState(false)

  useEffect(() => {
    const trimmed = query.trim()
    if (trimmed === "") {
      setResults(null)
      return
    }
    setSearching(true)
    const handle = setTimeout(() => {
      fetch(`/rounds/official-search?q=${encodeURIComponent(trimmed)}`, { credentials: "include" })
        .then((res) => (res.ok ? res.json() : []))
        .then((data: ApiFacility[]) => setResults(data))
        .catch(() => setResults([]))
        .finally(() => setSearching(false))
    }, 250)
    return () => clearTimeout(handle)
  }, [query])

  return (
    <div className="flex flex-col gap-8">
      <StepHeader title="Offisiell bane" description="Søk opp klubben i baneregisteret." />

      {query.trim() === "" && <NearbyClubs onSelectClub={onSelectClub} resolving={resolving} />}

      <div className="flex flex-col gap-3">
        <div className="relative">
          <Search aria-hidden="true" className="pointer-events-none absolute left-4 top-1/2 size-5 -translate-y-1/2 text-muted-foreground" />
          <Input
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder="Søk etter banenavn…"
            aria-label="Banenavn"
            className="h-14 rounded-2xl pl-12 text-lg"
          />
        </div>

        {query.trim() !== "" &&
          !searching &&
          results !== null &&
          (results.length === 0 ? (
            <HelperNote>Ingen klubber matcher «{query}». Prøv et annet søk.</HelperNote>
          ) : (
            <ul className="flex flex-col gap-3">
              {results.map((facility) => (
                <li key={facility.slug}>
                  <ClubRow
                    name={facility.name}
                    location={[facility.city, facility.county].filter(Boolean).join(", ")}
                    disabled={resolving}
                    onClick={() => onSelectClub(facility)}
                  />
                </li>
              ))}
            </ul>
          ))}
        {resolving && <p className="text-base text-muted-foreground">Henter baner …</p>}
      </div>
    </div>
  )
}

function ClubRow({ name, location, distance, disabled, onClick }: { name: string; location: string; distance?: string; disabled?: boolean; onClick: () => void }) {
  return (
    <button
      type="button"
      disabled={disabled}
      onClick={onClick}
      className="flex w-full items-center gap-4 rounded-2xl border border-border bg-card p-5 text-left shadow-md shadow-black/8 transition-colors hover:border-primary/50 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:opacity-60"
    >
      <div className="flex size-12 shrink-0 items-center justify-center rounded-xl bg-muted">
        <Building2 aria-hidden="true" className="size-6 text-muted-foreground" />
      </div>
      <div className="flex min-w-0 flex-1 flex-col">
        <span className="text-lg font-bold text-foreground">{name}</span>
        {location && (
          <span className="flex items-center gap-1.5 text-base text-muted-foreground">
            <MapPin aria-hidden="true" className="size-4" />
            {location}
          </span>
        )}
      </div>
      {distance && <span className="shrink-0 rounded-full bg-muted px-3 py-1 text-sm font-bold tabular-nums text-foreground">{distance}</span>}
    </button>
  )
}

function CourseList({ title, description, courses, onSelect }: { title: string; description: string; courses: Course[]; onSelect: (c: Course) => void }) {
  return (
    <div className="flex flex-col gap-8">
      <StepHeader title={title} description={description} />
      <ul className="flex flex-col gap-3">
        {courses.map((course) => (
          <li key={course.id}>
            <button
              type="button"
              onClick={() => onSelect(course)}
              className="flex w-full items-center gap-4 rounded-2xl border border-border bg-card p-5 text-left shadow-md shadow-black/8 transition-colors hover:border-primary/50 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
            >
              <div className="flex size-12 shrink-0 items-center justify-center rounded-xl bg-muted">
                <Flag aria-hidden="true" className="size-6 text-muted-foreground" />
              </div>
              <div className="flex min-w-0 flex-col">
                <span className="text-lg font-bold text-foreground">{course.name}</span>
                <span className="text-base text-muted-foreground">{course.tees.length} utslag</span>
              </div>
            </button>
          </li>
        ))}
        {courses.length === 0 && (
          <li className="rounded-2xl border border-border bg-muted/50 px-5 py-6 text-center text-base text-muted-foreground">
            Ingen 18-hulls baner registrert hos dette anlegget ennå.
          </li>
        )}
      </ul>
    </div>
  )
}

function OwnSearch({ onSelect, onCreate }: { onSelect: (c: ApiPersonalCourse) => void; onCreate: () => void }) {
  const [query, setQuery] = useState("")
  const [results, setResults] = useState<ApiPersonalCourse[]>([])

  useEffect(() => {
    let cancelled = false
    const timer = setTimeout(() => {
      fetch(`/personal-courses?q=${encodeURIComponent(query.trim())}`, { credentials: "include" })
        .then((res) => (res.ok ? res.json() : []))
        .then((data: ApiPersonalCourse[]) => {
          if (!cancelled) setResults(data)
        })
        .catch(() => {})
    }, 250)
    return () => {
      cancelled = true
      clearTimeout(timer)
    }
  }, [query])

  return (
    <div className="flex flex-col gap-8">
      <StepHeader title="Egen bane" description="Søk opp en personlig bane, eller opprett en ny." />

      <div className="relative">
        <Search aria-hidden="true" className="pointer-events-none absolute left-4 top-1/2 size-5 -translate-y-1/2 text-muted-foreground" />
        <Input
          autoFocus
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Søk etter banenavn…"
          aria-label="Banenavn"
          className="h-14 rounded-2xl pl-12 text-lg"
        />
      </div>

      {results.length > 0 ? (
        <ul className="flex flex-col gap-3">
          {results.map((course) => (
            <li key={course.id}>
              <button
                type="button"
                onClick={() => onSelect(course)}
                className="flex w-full items-center gap-4 rounded-2xl border border-border bg-card p-5 text-left shadow-md shadow-black/8 transition-colors hover:border-primary/50 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
              >
                <div className="flex size-12 shrink-0 items-center justify-center rounded-xl bg-muted">
                  <Flag aria-hidden="true" className="size-6 text-muted-foreground" />
                </div>
                <div className="flex min-w-0 flex-col">
                  <span className="text-lg font-bold text-foreground">{course.name}</span>
                </div>
              </button>
            </li>
          ))}
        </ul>
      ) : (
        <HelperNote>{query.trim() ? `Ingen egne baner matcher «${query}».` : "Skriv for å søke, eller opprett en ny bane under."}</HelperNote>
      )}

      <button
        type="button"
        onClick={onCreate}
        className="flex min-h-[44px] items-center justify-center gap-2 rounded-2xl border-2 border-dashed border-border bg-card/40 px-6 py-5 text-lg font-bold text-foreground transition-colors hover:border-primary/60 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
      >
        <Plus aria-hidden="true" className="size-5" />
        Opprett ny bane
      </button>
    </div>
  )
}

// --- Egen bane: opprett ny (ekte skjema -- porter fra tidligere versjon) ----

type CreateRating = { courseRating: string; slope: string; par: string }
type CreateTee = { id: string; name: string; men: boolean; women: boolean; menRating?: CreateRating; womenRating?: CreateRating }

function emptyHoles() {
  return Array.from({ length: 18 }, (_, i) => ({ hole: i + 1, par: "4", strokeIndex: String(i + 1) }))
}

function parseDecimal(value: string): number {
  return Number(value.replace(",", "."))
}

function OwnCreateStep({ onCreated, onCancel }: { onCreated: (course: Course) => void; onCancel: () => void }) {
  const [name, setName] = useState("")
  const [holes, setHoles] = useState(emptyHoles)
  const [tees, setTees] = useState<CreateTee[]>([{ id: `nt-${Date.now()}`, name: "", men: true, women: false }])
  const [submitting, setSubmitting] = useState(false)
  const [error, setError] = useState<string | null>(null)

  function updateHole(index: number, field: "par" | "strokeIndex", value: string) {
    setHoles((prev) => prev.map((h, i) => (i === index ? { ...h, [field]: value } : h)))
  }
  function updateTee(id: string, patch: Partial<CreateTee>) {
    setTees((prev) => prev.map((t) => (t.id === id ? { ...t, ...patch } : t)))
  }
  function addTee() {
    setTees((prev) => [...prev, { id: `nt-${Date.now()}`, name: "", men: false, women: false }])
  }
  function removeTee(id: string) {
    setTees((prev) => (prev.length > 1 ? prev.filter((t) => t.id !== id) : prev))
  }

  const indexesValid = useMemo(() => new Set(holes.map((h) => h.strokeIndex)).size === 18, [holes])
  const teesValid = tees.every(
    (t) =>
      t.name.trim() !== "" &&
      (t.men || t.women) &&
      (!t.men || (t.menRating && t.menRating.courseRating.trim() && t.menRating.slope.trim() && t.menRating.par.trim())) &&
      (!t.women || (t.womenRating && t.womenRating.courseRating.trim() && t.womenRating.slope.trim() && t.womenRating.par.trim())),
  )
  const canSubmit = name.trim() !== "" && indexesValid && teesValid && !submitting

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault()
    if (!canSubmit) return
    setSubmitting(true)
    setError(null)
    try {
      const body = {
        name: name.trim(),
        holes: holes.map((h) => ({ hole_number: h.hole, par: Number(h.par), stroke_index: Number(h.strokeIndex) })),
        tees: tees.map((t) => ({
          name: t.name.trim(),
          ratings: [
            ...(t.men && t.menRating
              ? [{ gender: "m", course_rating: parseDecimal(t.menRating.courseRating), slope_rating: Number(t.menRating.slope), par: Number(t.menRating.par) }]
              : []),
            ...(t.women && t.womenRating
              ? [{ gender: "f", course_rating: parseDecimal(t.womenRating.courseRating), slope_rating: Number(t.womenRating.slope), par: Number(t.womenRating.par) }]
              : []),
          ],
        })),
      }
      const res = await fetch("/personal-courses", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        credentials: "include",
        body: JSON.stringify(body),
      })
      if (!res.ok) throw new Error(`create: ${res.status}`)
      const created: { id: string; name: string } = await res.json()
      onCreated({
        id: created.id,
        name: created.name,
        tees: tees.map((t) => ({
          id: `${t.name.trim()}-${t.id}`,
          name: t.name.trim(),
          genders: [...(t.men ? (["m"] as ApiGender[]) : []), ...(t.women ? (["f"] as ApiGender[]) : [])],
        })),
      })
    } catch {
      setError("Klarte ikke å opprette banen. Sjekk at hull og utslag er fylt ut riktig.")
    } finally {
      setSubmitting(false)
    }
  }

  return (
    <form onSubmit={handleSubmit} className="flex flex-col gap-8">
      <StepHeader title="Opprett ny bane" description="Fyll inn banedata. Du kan endre detaljene senere." />

      {error && <p className="text-base font-medium text-destructive">{error}</p>}

      <div className="flex flex-col gap-2">
        <Label htmlFor="course-name" className="text-base font-semibold">
          Navn på banen
        </Label>
        <Input id="course-name" value={name} onChange={(e) => setName(e.target.value)} placeholder="F.eks. Hyttebanen" className="h-14 rounded-2xl text-lg" />
      </div>

      <div className="flex flex-col gap-3">
        <div className="flex flex-col gap-1">
          <h2 className="text-xl font-bold text-foreground">Hull</h2>
          <p className="text-base text-muted-foreground">Sett par og stroke-indeks for hvert hull.</p>
        </div>
        {!indexesValid && (
          <p role="alert" className="text-base font-medium text-destructive">
            Hver stroke-indeks (1–18) må brukes nøyaktig én gang.
          </p>
        )}
        <div className="overflow-hidden rounded-2xl border border-border">
          <div className="grid grid-cols-[auto_1fr_1fr] items-center gap-3 border-b border-border bg-muted/60 px-4 py-3 text-sm font-bold uppercase tracking-wide text-muted-foreground">
            <span className="w-16">Hull</span>
            <span>Par</span>
            <span>Stroke-indeks</span>
          </div>
          <ul>
            {holes.map((h, index) => (
              <li key={h.hole} className="grid grid-cols-[auto_1fr_1fr] items-center gap-3 border-b border-border px-4 py-2.5 last:border-b-0">
                <span className="w-16 text-base font-semibold text-foreground">Hull {h.hole}</span>
                <select
                  aria-label={`Par for hull ${h.hole}`}
                  value={h.par}
                  onChange={(e) => updateHole(index, "par", e.target.value)}
                  className="h-12 rounded-xl border border-input bg-background px-3 text-base font-semibold text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                >
                  {[3, 4, 5, 6].map((p) => (
                    <option key={p} value={String(p)}>
                      Par {p}
                    </option>
                  ))}
                </select>
                <select
                  aria-label={`Stroke-indeks for hull ${h.hole}`}
                  value={h.strokeIndex}
                  onChange={(e) => updateHole(index, "strokeIndex", e.target.value)}
                  className="h-12 rounded-xl border border-input bg-background px-3 text-base font-semibold text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                >
                  {Array.from({ length: 18 }, (_, i) => i + 1).map((si) => (
                    <option key={si} value={String(si)}>
                      {si}
                    </option>
                  ))}
                </select>
              </li>
            ))}
          </ul>
        </div>
      </div>

      <div className="flex flex-col gap-4">
        <div className="flex flex-col gap-1">
          <h2 className="text-xl font-bold text-foreground">Utslag</h2>
          <p className="text-base text-muted-foreground">Legg til ett eller flere utslag med rating.</p>
        </div>

        {tees.map((t, index) => (
          <TeeEditor key={t.id} tee={t} index={index} canRemove={tees.length > 1} onChange={(patch) => updateTee(t.id, patch)} onRemove={() => removeTee(t.id)} />
        ))}

        <button
          type="button"
          onClick={addTee}
          className="flex min-h-[44px] items-center justify-center gap-2 rounded-2xl border-2 border-dashed border-border bg-card/40 px-6 py-4 text-base font-bold text-foreground transition-colors hover:border-primary/60 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
        >
          <Plus aria-hidden="true" className="size-5" />
          Legg til utslag
        </button>
      </div>

      <div className="flex flex-col gap-3 sm:flex-row-reverse">
        <Button type="submit" size="lg" disabled={!canSubmit} className="h-14 flex-1 rounded-2xl text-lg font-bold shadow-sm">
          {submitting ? "Oppretter…" : "Opprett bane og fortsett"}
        </Button>
        <Button type="button" variant="ghost" size="lg" onClick={onCancel} className="h-14 rounded-2xl px-8 text-base font-semibold">
          Avbryt
        </Button>
      </div>
    </form>
  )
}

function TeeEditor({ tee, index, canRemove, onChange, onRemove }: { tee: CreateTee; index: number; canRemove: boolean; onChange: (patch: Partial<CreateTee>) => void; onRemove: () => void }) {
  return (
    <div className="flex flex-col gap-4 rounded-2xl border border-border bg-card p-5 shadow-md shadow-black/8">
      <div className="flex items-end gap-3">
        <div className="flex flex-1 flex-col gap-2">
          <Label htmlFor={`tee-name-${tee.id}`} className="text-base font-semibold">
            Navn på utslag {index + 1}
          </Label>
          <Input id={`tee-name-${tee.id}`} value={tee.name} onChange={(e) => onChange({ name: e.target.value })} placeholder="F.eks. Gul" className="h-12 rounded-xl text-base" />
        </div>
        {canRemove && (
          <Button type="button" variant="outline" onClick={onRemove} className="h-12 rounded-xl px-4 text-base font-semibold">
            Fjern
          </Button>
        )}
      </div>

      <RatingToggle
        label="Herre-rating"
        enabled={tee.men}
        rating={tee.menRating}
        onToggle={(men) => onChange({ men, menRating: men ? tee.menRating ?? { courseRating: "", slope: "", par: "" } : tee.menRating })}
        onRatingChange={(menRating) => onChange({ menRating })}
        idPrefix={`men-${tee.id}`}
      />
      <RatingToggle
        label="Dame-rating"
        enabled={tee.women}
        rating={tee.womenRating}
        onToggle={(women) => onChange({ women, womenRating: women ? tee.womenRating ?? { courseRating: "", slope: "", par: "" } : tee.womenRating })}
        onRatingChange={(womenRating) => onChange({ womenRating })}
        idPrefix={`women-${tee.id}`}
      />
    </div>
  )
}

function RatingToggle({
  label,
  enabled,
  rating,
  onToggle,
  onRatingChange,
  idPrefix,
}: {
  label: string
  enabled: boolean
  rating?: CreateRating
  onToggle: (enabled: boolean) => void
  onRatingChange: (rating: CreateRating) => void
  idPrefix: string
}) {
  const current = rating ?? { courseRating: "", slope: "", par: "" }
  function update(field: keyof CreateRating, value: string) {
    onRatingChange({ ...current, [field]: value })
  }
  return (
    <div className="flex flex-col gap-3 rounded-xl border border-border bg-background/60 p-4">
      <div className="flex items-center justify-between gap-4">
        <Label htmlFor={idPrefix} className="text-base font-semibold text-foreground">
          {label}
        </Label>
        <Switch id={idPrefix} checked={enabled} onCheckedChange={onToggle} />
      </div>
      {enabled && (
        <div className="grid grid-cols-3 gap-3">
          <div className="flex flex-col gap-1.5">
            <Label htmlFor={`${idPrefix}-cr`} className="text-sm font-medium text-muted-foreground">
              Course Rating
            </Label>
            <Input id={`${idPrefix}-cr`} inputMode="decimal" value={current.courseRating} onChange={(e) => update("courseRating", e.target.value)} placeholder="71,2" className="h-12 rounded-xl text-base" />
          </div>
          <div className="flex flex-col gap-1.5">
            <Label htmlFor={`${idPrefix}-slope`} className="text-sm font-medium text-muted-foreground">
              Slope
            </Label>
            <Input id={`${idPrefix}-slope`} inputMode="numeric" value={current.slope} onChange={(e) => update("slope", e.target.value)} placeholder="132" className="h-12 rounded-xl text-base" />
          </div>
          <div className="flex flex-col gap-1.5">
            <Label htmlFor={`${idPrefix}-par`} className="text-sm font-medium text-muted-foreground">
              Par
            </Label>
            <Input id={`${idPrefix}-par`} inputMode="numeric" value={current.par} onChange={(e) => update("par", e.target.value)} placeholder="72" className="h-12 rounded-xl text-base" />
          </div>
        </div>
      )}
    </div>
  )
}

function Step1Fields({
  course,
  ownGender,
  roundName,
  setRoundName,
  ownerTeeId,
  setOwnerTeeId,
  date,
  setDate,
  time,
  setTime,
  startHole,
  setStartHole,
}: {
  course: Course
  ownGender: ApiGender
  roundName: string
  setRoundName: (v: string) => void
  ownerTeeId: string
  setOwnerTeeId: (v: string) => void
  date: string
  setDate: (v: string) => void
  time: string
  setTime: (v: string) => void
  startHole: string
  setStartHole: (v: string) => void
} & Record<string, unknown>) {
  const compatibleTees = course.tees.filter((t) => t.genders.includes(ownGender))

  return (
    <div className="flex flex-col gap-8">
      <div className="flex items-center gap-4 rounded-2xl border border-primary/30 bg-primary/5 p-4">
        <div className="flex size-12 shrink-0 items-center justify-center rounded-xl bg-primary/15">
          <Flag aria-hidden="true" className="size-6 text-primary" />
        </div>
        <div className="flex min-w-0 flex-col">
          <span className="text-sm font-bold uppercase tracking-wide text-primary">Valgt bane</span>
          <span className="truncate text-lg font-bold text-foreground">{course.name}</span>
        </div>
      </div>

      <div className="flex flex-col gap-2">
        <Label htmlFor="round-name" className="text-base font-semibold">
          Navn på runden (valgfritt)
        </Label>
        <Input id="round-name" value={roundName} onChange={(e) => setRoundName(e.target.value)} placeholder={course.name} maxLength={200} className="h-14 rounded-2xl text-lg" />
      </div>

      {compatibleTees.length === 0 ? (
        <p role="alert" className="text-base font-medium text-destructive">
          Denne banen har ingen registrert rating for ditt kjønn på noe utslag -- HCP-sporing er ikke mulig for denne runden.
        </p>
      ) : (
        <div className="flex flex-col gap-3">
          <SectionTitle title="Utslag" />
          <div className="flex flex-wrap gap-2">
            {compatibleTees.map((t) => (
              <Pill key={t.id} selected={t.id === ownerTeeId} onClick={() => setOwnerTeeId(t.id)}>
                {t.name}
              </Pill>
            ))}
          </div>
        </div>
      )}

      <div className="grid gap-4 sm:grid-cols-2">
        <div className="flex flex-col gap-2">
          <Label htmlFor="date" className="text-base font-semibold">
            Dato
          </Label>
          <Input id="date" type="date" value={date} onChange={(e) => setDate(e.target.value)} className="h-14 rounded-2xl text-lg" />
        </div>
        <div className="flex flex-col gap-2">
          <Label htmlFor="time" className="text-base font-semibold">
            Utslagstid (valgfritt)
          </Label>
          <Input id="time" type="time" value={time} onChange={(e) => setTime(e.target.value)} className="h-14 rounded-2xl text-lg" />
        </div>
      </div>

      <div className="flex flex-col gap-2">
        <Label htmlFor="start-hole" className="text-base font-semibold">
          Starthull
        </Label>
        <select
          id="start-hole"
          value={startHole}
          onChange={(e) => setStartHole(e.target.value)}
          className="h-14 rounded-2xl border border-input bg-background px-4 text-lg font-semibold text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
        >
          {Array.from({ length: 18 }, (_, i) => i + 1).map((h) => (
            <option key={h} value={String(h)}>
              Hull {h}
            </option>
          ))}
        </select>
      </div>
    </div>
  )
}

// --- Step 2: Spilleform ------------------------------------------------------

function Step2(props: {
  statLevel: StatLevel
  setStatLevel: (v: StatLevel) => void
  format: FormatId
  setFormat: (v: FormatId) => void
  holes: 9 | 18
  setHoles: (v: 9 | 18) => void
  matchExcludeHcp: boolean
  setMatchExcludeHcp: (v: boolean) => void
  skinsScoring: "netto" | "brutto"
  setSkinsScoring: (v: "netto" | "brutto") => void
  skinsTie: "roll" | "split"
  setSkinsTie: (v: "roll" | "split") => void
  bbbBonus: boolean
  setBbbBonus: (v: boolean) => void
  shambleBest: number
  setShambleBest: (v: number) => void
  advOpen: boolean
  setAdvOpen: (v: boolean) => void
  useHcp: boolean
  setUseHcp: (v: boolean) => void
  useCourseHcp: boolean
  setUseCourseHcp: (v: boolean) => void
  useMatchplayHcp: boolean
  setUseMatchplayHcp: (v: boolean) => void
  hcpPercent: string
  setHcpPercent: (v: string) => void
}) {
  const {
    statLevel, setStatLevel, format, setFormat, holes, setHoles,
    matchExcludeHcp, setMatchExcludeHcp, skinsScoring, setSkinsScoring, skinsTie, setSkinsTie,
    bbbBonus, setBbbBonus, shambleBest, setShambleBest,
    advOpen, setAdvOpen, useHcp, setUseHcp, useCourseHcp, setUseCourseHcp,
    useMatchplayHcp, setUseMatchplayHcp, hcpPercent, setHcpPercent,
  } = props

  const selectedFormat = FORMATS.find((f) => f.id === format)!
  const twoSided = TWO_SIDED.has(format)

  return (
    <div className="flex flex-col gap-8">
      <StepHeader title="Spilleform" />

      <div className="flex flex-col gap-3">
        <SectionTitle title="Statistikk for deg selv" />
        <div className="flex flex-col gap-3">
          {STAT_LEVELS.map((s) => (
            <ChoiceCard key={s.id} selected={statLevel === s.id} onClick={() => setStatLevel(s.id)} title={s.label} description={s.desc} />
          ))}
        </div>
      </div>

      <div className="flex flex-col gap-3">
        <SectionTitle title="Spilleform" />
        <div className="grid grid-cols-2 gap-2 md:grid-cols-4">
          {FORMATS.map((f) => {
            const selected = f.id === format
            return (
              <button
                key={f.id}
                type="button"
                onClick={() => setFormat(f.id)}
                aria-pressed={selected}
                className={cn(
                  "flex min-h-[56px] items-center justify-center rounded-2xl border-2 px-3 py-2.5 text-center text-base font-bold leading-tight transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
                  selected ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground hover:border-primary/50 hover:bg-accent/40",
                )}
              >
                {f.label}
              </button>
            )
          })}
        </div>
        <p className="rounded-2xl bg-muted/60 px-4 py-3 text-base leading-relaxed text-foreground text-pretty">
          <span className="font-bold">{selectedFormat.label}:</span> {selectedFormat.desc}
        </p>
      </div>

      <FormatConfig
        format={format}
        matchExcludeHcp={matchExcludeHcp}
        setMatchExcludeHcp={setMatchExcludeHcp}
        skinsScoring={skinsScoring}
        setSkinsScoring={setSkinsScoring}
        skinsTie={skinsTie}
        setSkinsTie={setSkinsTie}
        bbbBonus={bbbBonus}
        setBbbBonus={setBbbBonus}
        shambleBest={shambleBest}
        setShambleBest={setShambleBest}
      />

      <div className="overflow-hidden rounded-2xl border border-border bg-muted/30">
        <button
          type="button"
          onClick={() => setAdvOpen(!advOpen)}
          aria-expanded={advOpen}
          className="flex min-h-[56px] w-full items-center gap-2.5 px-4 py-3 text-left transition-colors hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset"
        >
          <Sparkles aria-hidden="true" className="size-5 text-muted-foreground" />
          <span className="flex-1 text-base font-bold text-foreground">Avanserte handicap-innstillinger</span>
          <ChevronDown aria-hidden="true" className={cn("size-5 text-muted-foreground transition-transform", advOpen && "rotate-180")} />
        </button>
        {advOpen && (
          <div className="flex flex-col gap-5 border-t border-border px-4 py-5">
            <ToggleRow label="Bruk handicap" checked={useHcp} onChange={setUseHcp} />
            {useHcp && !ORDINARY_FORMATS.has(format) && (
              <ToggleRow label="Bruk course handicap-justering" checked={useCourseHcp} onChange={setUseCourseHcp} />
            )}
            {useHcp && twoSided && (
              <div className="flex flex-col gap-1.5">
                <ToggleRow label="Bruk matchplay-handicap" checked={useMatchplayHcp} onChange={setUseMatchplayHcp} />
                <p className="px-1 text-sm leading-relaxed text-muted-foreground text-pretty">
                  Fordeler slagene som en differanse mellom sidene i stedet for hvert lags fulle handicap — slås av
                  spilles matchen med rå brutto/netto per side.
                </p>
              </div>
            )}
            {useHcp && (
              <div className="flex flex-col gap-2">
                <Label htmlFor="hcp-percent" className="text-base font-semibold">
                  HCP-prosent (valgfritt)
                </Label>
                <Input id="hcp-percent" inputMode="decimal" value={hcpPercent} onChange={(e) => setHcpPercent(e.target.value)} placeholder="Standard for format" className="h-14 rounded-2xl text-lg" />
              </div>
            )}
          </div>
        )}
      </div>

      <div className="flex flex-col gap-3">
        <SectionTitle title="Antall hull" />
        <div className="grid grid-cols-2 gap-3">
          {([9, 18] as const).map((h) => {
            const selected = holes === h
            return (
              <button
                key={h}
                type="button"
                onClick={() => setHoles(h)}
                aria-pressed={selected}
                className={cn(
                  "min-h-[64px] rounded-2xl border-2 text-xl font-extrabold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
                  selected ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground hover:border-primary/50 hover:bg-accent/40",
                )}
              >
                {h} hull
              </button>
            )
          })}
        </div>
      </div>
    </div>
  )
}

function ToggleRow({ label, checked, onChange }: { label: string; checked: boolean; onChange: (v: boolean) => void }) {
  return (
    <div className="flex min-h-[44px] items-center justify-between gap-4">
      <Label className="text-base font-semibold text-foreground">{label}</Label>
      <Switch checked={checked} onCheckedChange={onChange} />
    </div>
  )
}

function FormatConfig({
  format,
  matchExcludeHcp,
  setMatchExcludeHcp,
  skinsScoring,
  setSkinsScoring,
  skinsTie,
  setSkinsTie,
  bbbBonus,
  setBbbBonus,
  shambleBest,
  setShambleBest,
}: {
  format: FormatId
  matchExcludeHcp: boolean
  setMatchExcludeHcp: (v: boolean) => void
  skinsScoring: "netto" | "brutto"
  setSkinsScoring: (v: "netto" | "brutto") => void
  skinsTie: "roll" | "split"
  setSkinsTie: (v: "roll" | "split") => void
  bbbBonus: boolean
  setBbbBonus: (v: boolean) => void
  shambleBest: number
  setShambleBest: (v: number) => void
}) {
  if (format === "match") {
    return (
      <CheckRow
        checked={matchExcludeHcp}
        onChange={setMatchExcludeHcp}
        label="Ekskluder denne runden fra mitt faktiske HCP"
        helper="Anbefalt for match -- konsederte hull gir ofte et upålitelig grunnlag for HCP-beregning. Du kan endre dette senere."
      />
    )
  }

  if (format === "skins") {
    return (
      <div className="flex flex-col gap-5">
        <div className="flex flex-col gap-2">
          <Label className="text-base font-semibold text-foreground">Poengberegning</Label>
          <SegChoice
            label="Poengberegning"
            value={skinsScoring}
            onChange={setSkinsScoring}
            options={[
              { value: "netto", label: "Netto (m/HCP)" },
              { value: "brutto", label: "Brutto" },
            ]}
          />
        </div>
        <div className="flex flex-col gap-2">
          <Label className="text-base font-semibold text-foreground">Ved uavgjort hull</Label>
          <SegChoice
            label="Ved uavgjort hull"
            value={skinsTie}
            onChange={setSkinsTie}
            options={[
              { value: "roll", label: "Rullerer over" },
              { value: "split", label: "Deles likt" },
            ]}
          />
        </div>
        <HelperNote>Skins krever minst 3 spillere i runden -- legg til medspillere etter at runden er opprettet.</HelperNote>
      </div>
    )
  }

  if (format === "bbb") {
    return (
      <CheckRow
        checked={bbbBonus}
        onChange={setBbbBonus}
        label={'Bonuspoeng ved "sveip"'}
        helper="Ekstra poeng til en spiller som vinner alle tre kategoriene (bingo, bango og bongo) på samme hull."
      />
    )
  }

  if (format === "shamble") {
    return (
      <div className="flex flex-col gap-2">
        <Label className="text-base font-semibold text-foreground">Beste N av laget teller per hull</Label>
        <NumberButtons label="Beste N av laget teller per hull" values={[1, 2, 3, 4]} value={shambleBest} onChange={setShambleBest} />
        <p className="text-sm leading-relaxed text-muted-foreground text-pretty">
          2-4 spillere på laget, alle slår ut og velger beste utslag -- deretter spiller alle egen ball inn. Legg til
          medspillere etter opprettelse.
        </p>
      </div>
    )
  }

  if (format === "moneyball") {
    return <HelperNote>Nøyaktig 4 spillere -- du setter opp hvem som er "money ball" på hvilket hull i neste steg.</HelperNote>
  }

  if (format === "copenhagen") {
    return <HelperNote>Nøyaktig 3 spillere.</HelperNote>
  }

  if (TWO_SIDED.has(format)) {
    return <HelperNote>Du setter opp lagene i neste steg.</HelperNote>
  }

  return null
}

// --- Step 3: Spillere ---------------------------------------------------------

function Step3({
  course,
  players,
  setPlayers,
  roundStatLevel,
}: {
  course: Course
  players: Player[]
  setPlayers: React.Dispatch<React.SetStateAction<Player[]>>
  roundStatLevel: StatLevel
}) {
  const [editingId, setEditingId] = useState<string | null>(null)
  const [adding, setAdding] = useState(false)

  function updatePlayer(id: string, patch: Partial<Player>) {
    setPlayers((prev) => prev.map((p) => (p.id === id ? { ...p, ...patch } : p)))
  }
  function removePlayer(id: string) {
    setPlayers((prev) => prev.filter((p) => p.id !== id))
    if (editingId === id) setEditingId(null)
  }
  function addPlayer(p: Player) {
    setPlayers((prev) => [...prev, p])
    setAdding(false)
  }

  return (
    <div className="flex flex-col gap-6">
      <StepHeader title="Spillere" />

      <ul className="flex flex-col gap-3">
        {players.map((p) => (
          <li key={p.id}>
            <PlayerCard player={p} course={course} editing={editingId === p.id} onToggleEdit={() => setEditingId(editingId === p.id ? null : p.id)} onRemove={() => removePlayer(p.id)} onChange={(patch) => updatePlayer(p.id, patch)} />
          </li>
        ))}
      </ul>

      {adding ? (
        <AddPlayerPanel onCancel={() => setAdding(false)} onAdd={addPlayer} roundStatLevel={roundStatLevel} existingNames={players.map((p) => p.name)} />
      ) : (
        <button
          type="button"
          onClick={() => setAdding(true)}
          className="flex min-h-[44px] items-center justify-center gap-2 rounded-2xl border-2 border-dashed border-border bg-card/40 px-6 py-5 text-lg font-bold text-foreground transition-colors hover:border-primary/60 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
        >
          <Plus aria-hidden="true" className="size-5" />
          Medspiller
        </button>
      )}
    </div>
  )
}

// Egen komponent for fornavn/etternavn-redigering av en allerede lagt-til
// gjest -- lokal state (splittet fra player.name ved mount) fordi feltene
// må kunne redigeres uavhengig, men det lagrede player.name forblir det
// sammensatte "Fornavn Etternavn"-strenget resten av veiviseren bruker.
function GuestNameFields({ player, onChange }: { player: Player; onChange: (patch: Partial<Player>) => void }) {
  const initial = useMemo(() => splitName(player.name), [player.id])
  const [firstName, setFirstName] = useState(initial.first)
  const [lastName, setLastName] = useState(initial.last)

  function commit(first: string, last: string) {
    onChange({ name: `${first.trim()} ${last.trim()}`.trim() })
  }

  return (
    <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
      <div className="flex flex-col gap-2">
        <Label htmlFor={`first-name-${player.id}`} className="text-base font-semibold">
          Fornavn
        </Label>
        <Input
          id={`first-name-${player.id}`}
          value={firstName}
          onChange={(e) => {
            setFirstName(e.target.value)
            commit(e.target.value, lastName)
          }}
          className="h-12 rounded-xl text-base"
        />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor={`last-name-${player.id}`} className="text-base font-semibold">
          Etternavn <span className="font-normal text-muted-foreground">(valgfritt)</span>
        </Label>
        <Input
          id={`last-name-${player.id}`}
          value={lastName}
          onChange={(e) => {
            setLastName(e.target.value)
            commit(firstName, e.target.value)
          }}
          className="h-12 rounded-xl text-base"
        />
      </div>
    </div>
  )
}

function PlayerCard({
  player,
  course,
  editing,
  onToggleEdit,
  onRemove,
  onChange,
}: {
  player: Player
  course: Course
  editing: boolean
  onToggleEdit: () => void
  onRemove: () => void
  onChange: (patch: Partial<Player>) => void
}) {
  const isLinked = Boolean(player.userId)
  // Kjønnet til en spiller funnet via søk er IKKE kjent før de faktisk er
  // lagt til (PersonMatch bærer ikke gender) -- utslagslisten kan derfor
  // ikke filtreres for dem ennå, ulikt gjester (som velger kjønn selv her).
  // "Annet" har ingen tee-rating i det hele tatt (kun m/f finnes) -- gir
  // da bevisst en tom liste, samme begrensning som resten av appen.
  const guestApiGender = apiGenderOf(player.gender)
  const teeOptions =
    player.isGuest
      ? guestApiGender === "x"
        ? []
        : course.tees.filter((t) => t.genders.includes(guestApiGender))
      : course.tees

  return (
    <div className="rounded-2xl border border-border bg-card shadow-md shadow-black/8">
      <div className="flex items-center gap-3 p-4">
        <Avatar name={player.name} />
        <div className="flex min-w-0 flex-1 flex-col gap-1">
          <div className="flex flex-wrap items-center gap-2">
            <span className="text-lg font-bold text-foreground">{player.name}</span>
            {player.isOwner && (
              <>
                <Badge>Deg</Badge>
                <Badge>Eier</Badge>
              </>
            )}
            {player.isGuest && <Badge tone="muted">Gjest</Badge>}
          </div>
          <span className="text-base text-muted-foreground">
            Utslag: {teeName(course, player.teeId)} · HCP: {player.hcp || "—"}
          </span>
        </div>
        <div className="flex shrink-0 items-center gap-1">
          <Button type="button" variant="outline" onClick={onToggleEdit} className="h-11 rounded-xl px-4 text-base font-semibold">
            {editing ? "Lukk" : "Rediger"}
          </Button>
          {!player.isOwner && (
            <button
              type="button"
              onClick={onRemove}
              aria-label={`Fjern ${player.name}`}
              className="flex size-11 items-center justify-center rounded-xl text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
            >
              <X aria-hidden="true" className="size-5" />
            </button>
          )}
        </div>
      </div>

      {editing && (
        <div className="flex flex-col gap-5 border-t border-border p-4">
          {player.isGuest && (
            <>
              <GuestNameFields player={player} onChange={onChange} />
              <div className="flex flex-col gap-2">
                <Label className="text-base font-semibold">Kjønn</Label>
                <SegChoice<Gender>
                  label="Kjønn"
                  value={player.gender}
                  onChange={(g) => onChange({ gender: g })}
                  options={[
                    { value: "mann", label: "Mann" },
                    { value: "kvinne", label: "Kvinne" },
                    { value: "annet", label: "Annet" },
                  ]}
                />
              </div>
              <div className="flex flex-col gap-2">
                <Label htmlFor={`email-${player.id}`} className="text-base font-semibold">
                  E-post (valgfritt)
                </Label>
                <Input id={`email-${player.id}`} type="email" value={player.email ?? ""} onChange={(e) => onChange({ email: e.target.value })} className="h-12 rounded-xl text-base" />
              </div>
            </>
          )}

          <div className="flex flex-col gap-2">
            <Label className="text-base font-semibold">Utslag</Label>
            <div className="flex flex-wrap gap-2">
              {teeOptions.map((t) => (
                <Pill key={t.id} selected={t.id === player.teeId} onClick={() => onChange({ teeId: t.id })}>
                  {t.name}
                </Pill>
              ))}
            </div>
            {isLinked && (
              <p className="text-sm text-muted-foreground">
                Kjønn er ikke kjent før spilleren er lagt til runden -- velg utslag med omhu.
              </p>
            )}
          </div>

          {player.isGuest ? (
            <div className="flex flex-col gap-2">
              <Label htmlFor={`hcp-${player.id}`} className="text-base font-semibold">
                HCP
              </Label>
              <Input id={`hcp-${player.id}`} inputMode="decimal" value={player.hcp} onChange={(e) => onChange({ hcp: e.target.value })} placeholder="F.eks. 14,2" className="h-12 rounded-xl text-base" />
            </div>
          ) : (
            <div className="flex flex-col gap-1">
              <Label className="text-base font-semibold">HCP</Label>
              <p className="text-base text-muted-foreground">
                {player.hcp ? `Hentes fra profilen (HCP ${player.hcp})` : "Hentes fra profilen når spilleren legges til."}
              </p>
            </div>
          )}

          <div className="flex flex-col gap-2">
            <Label className="text-base font-semibold">Statistikk</Label>
            <SegChoice<StatLevel>
              label="Statistikk"
              value={player.statLevel}
              onChange={(s) => onChange({ statLevel: s })}
              options={[
                { value: "kun-slag", label: "Kun slag" },
                { value: "slag-putter", label: "Slag og putter" },
                { value: "all", label: "All statistikk" },
              ]}
            />
          </div>
        </div>
      )}
    </div>
  )
}

function Badge({ children, tone = "primary" }: { children: React.ReactNode; tone?: "primary" | "muted" }) {
  return <span className={cn("rounded-full px-2.5 py-0.5 text-sm font-bold", tone === "primary" ? "bg-primary/15 text-primary" : "bg-muted text-muted-foreground")}>{children}</span>
}

function AddPlayerPanel({
  onCancel,
  onAdd,
  roundStatLevel,
  existingNames,
}: {
  onCancel: () => void
  onAdd: (p: Player) => void
  roundStatLevel: StatLevel
  existingNames: string[]
}) {
  const [mode, setMode] = useState<"search" | "guest">("search")
  const [query, setQuery] = useState("")
  const [results, setResults] = useState<PersonMatch[]>([])
  const [searching, setSearching] = useState(false)

  const [gFirstName, setGFirstName] = useState("")
  const [gLastName, setGLastName] = useState("")
  const [gEmail, setGEmail] = useState("")
  const [gGender, setGGender] = useState<Gender>("mann")
  const [gHcp, setGHcp] = useState("")
  const [gStat, setGStat] = useState<StatLevel>(roundStatLevel)
  const [knownGuest, setKnownGuest] = useState<KnownGuest | null>(null)
  const [knownGuestDismissed, setKnownGuestDismissed] = useState(false)

  // "Vi kjenner igjen denne e-posten" -- kun søk i EGNE tidligere
  // registrerte gjester (håndhevet server-side, se find_known_guest i
  // app/routers/rounds.py -- aldri et globalt oppslag). Samme mønster som
  // round-detail.tsx sin AddGuestForm.
  useEffect(() => {
    const trimmed = gEmail.trim()
    setKnownGuestDismissed(false)
    if (trimmed.length < 5 || !trimmed.includes("@")) {
      setKnownGuest(null)
      return
    }
    const handle = setTimeout(() => {
      fetch(`/rounds/guests/known?email=${encodeURIComponent(trimmed)}`, { credentials: "include" })
        .then((res) => (res.ok ? res.json() : null))
        .then((data: KnownGuest | null) => setKnownGuest(data))
        .catch(() => setKnownGuest(null))
    }, 350)
    return () => clearTimeout(handle)
  }, [gEmail])

  function useKnownGuest() {
    if (!knownGuest) return
    setGFirstName(knownGuest.guest_first_name)
    setGLastName(knownGuest.guest_last_name ?? "")
    if (knownGuest.gender) setGGender(uiGenderOf(knownGuest.gender))
    if (knownGuest.handicap_index !== null) setGHcp(String(knownGuest.handicap_index).replace(".", ","))
    setKnownGuestDismissed(true)
  }

  // Navnet fra søkefeltet skal følge med når brukeren i stedet velger
  // "Legg til uten konto (gjest)" fordi personen ikke fantes.
  function switchToGuestMode() {
    const split = splitName(query)
    setGFirstName(split.first)
    setGLastName(split.last)
    setMode("guest")
  }

  useEffect(() => {
    const trimmed = query.trim()
    if (trimmed.length < 2) {
      setResults([])
      setSearching(false)
      return
    }
    setSearching(true)
    const handle = setTimeout(() => {
      fetch(`/people/search?q=${encodeURIComponent(trimmed)}`, { credentials: "include" })
        .then((res) => (res.ok ? res.json() : []))
        .then((data: PersonMatch[]) => {
          const taken = new Set(existingNames.map((n) => n.toLowerCase()))
          setResults(data.filter((p) => !taken.has(`${p.first_name} ${p.last_name}`.toLowerCase())))
        })
        .catch(() => setResults([]))
        .finally(() => setSearching(false))
    }, 250)
    return () => clearTimeout(handle)
  }, [query, existingNames])

  return (
    <div className="flex flex-col gap-5 rounded-2xl border border-border bg-card p-5 shadow-md shadow-black/8">
      <div className="flex items-center justify-between gap-3">
        <h3 className="text-lg font-bold text-foreground">{mode === "search" ? "Søk etter medspiller" : "Legg til gjest"}</h3>
        <button type="button" onClick={onCancel} aria-label="Lukk" className="flex size-11 items-center justify-center rounded-xl text-muted-foreground transition-colors hover:bg-accent/40 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
          <X aria-hidden="true" className="size-5" />
        </button>
      </div>

      {mode === "search" ? (
        <>
          <div className="relative">
            <Search aria-hidden="true" className="pointer-events-none absolute left-4 top-1/2 size-5 -translate-y-1/2 text-muted-foreground" />
            <Input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Navn på medspilleren" aria-label="Navn på medspilleren" className="h-14 rounded-2xl pl-12 text-lg" />
          </div>
          {query.trim().length > 0 && query.trim().length < 2 && <p className="text-sm text-muted-foreground">Skriv minst 2 tegn for å søke.</p>}

          {results.length > 0 ? (
            <ul className="flex flex-col gap-2">
              {results.map((r) => (
                <li key={r.id}>
                  <button
                    type="button"
                    onClick={() =>
                      onAdd({
                        id: newId("player"),
                        userId: r.id,
                        name: `${r.first_name} ${r.last_name}`,
                        gender: "mann",
                        teeId: "",
                        hcp: r.handicap_index !== null ? r.handicap_index.toFixed(1).replace(".", ",") : "",
                        statLevel: roundStatLevel,
                      })
                    }
                    className="flex w-full items-center gap-3 rounded-2xl border border-border bg-card p-3 text-left transition-colors hover:border-primary/50 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
                  >
                    <Avatar name={`${r.first_name} ${r.last_name}`} />
                    <div className="flex min-w-0 flex-1 flex-col">
                      <span className="text-base font-bold text-foreground">
                        {r.first_name} {r.last_name}
                      </span>
                      <span className="text-sm text-muted-foreground">
                        {r.home_club}
                        {r.home_club && r.handicap_index !== null && " · "}
                        {r.handicap_index !== null && `HCP ${r.handicap_index.toFixed(1).replace(".", ",")}`}
                      </span>
                    </div>
                    <Plus aria-hidden="true" className="size-5 text-primary" />
                  </button>
                </li>
              ))}
            </ul>
          ) : (
            !searching && query.trim().length >= 2 && <p className="text-base text-muted-foreground">Fant ingen med det navnet.</p>
          )}

          <button type="button" onClick={switchToGuestMode} className="min-h-[44px] text-left text-base font-bold text-primary underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
            Legg til uten konto (gjest)
          </button>
        </>
      ) : (
        <>
          <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
            <div className="flex flex-col gap-2">
              <Label htmlFor="guest-first-name" className="text-base font-semibold">
                Fornavn
              </Label>
              <Input
                id="guest-first-name"
                autoFocus
                value={gFirstName}
                onChange={(e) => setGFirstName(e.target.value)}
                placeholder="Fornavn"
                className="h-14 rounded-2xl text-lg"
              />
            </div>
            <div className="flex flex-col gap-2">
              <Label htmlFor="guest-last-name" className="text-base font-semibold">
                Etternavn <span className="font-normal text-muted-foreground">(valgfritt)</span>
              </Label>
              <Input
                id="guest-last-name"
                value={gLastName}
                onChange={(e) => setGLastName(e.target.value)}
                placeholder="Etternavn"
                className="h-14 rounded-2xl text-lg"
              />
            </div>
          </div>

          <div className="flex flex-col gap-2">
            <Label htmlFor="guest-email" className="text-base font-semibold">
              E-post <span className="font-normal text-muted-foreground">(valgfritt)</span>
            </Label>
            <Input
              id="guest-email"
              type="email"
              value={gEmail}
              onChange={(e) => setGEmail(e.target.value)}
              placeholder="For scorekort og statistikk etter runden"
              className="h-14 rounded-2xl text-lg"
            />
            {knownGuest && !knownGuestDismissed && (
              <div className="flex flex-col gap-2 rounded-2xl border border-primary/40 bg-primary/5 p-3 sm:flex-row sm:items-center sm:justify-between">
                <p className="text-sm text-foreground">
                  Vi fant <span className="font-bold">{knownGuest.guest_first_name} {knownGuest.guest_last_name}</span>{" "}
                  fra en tidligere runde du satte opp.
                </p>
                <Button type="button" size="sm" onClick={useKnownGuest} className="h-10 shrink-0 rounded-xl px-4 text-sm font-bold">
                  Bruk disse opplysningene
                </Button>
              </div>
            )}
          </div>

          <div className="flex flex-col gap-2">
            <Label className="text-base font-semibold">Kjønn</Label>
            <SegChoice<Gender>
              label="Kjønn"
              value={gGender}
              onChange={setGGender}
              options={[
                { value: "mann", label: "Mann" },
                { value: "kvinne", label: "Kvinne" },
                { value: "annet", label: "Annet" },
              ]}
            />
          </div>

          <div className="flex flex-col gap-2">
            <Label htmlFor="guest-hcp" className="text-base font-semibold">
              HCP (valgfritt)
            </Label>
            <Input id="guest-hcp" inputMode="decimal" value={gHcp} onChange={(e) => setGHcp(e.target.value)} placeholder="F.eks. 18,0" className="h-14 rounded-2xl text-lg" />
          </div>

          <div className="flex flex-col gap-2">
            <Label className="text-base font-semibold">Statistikk for denne spilleren</Label>
            <SegChoice<StatLevel>
              label="Statistikk for denne spilleren"
              value={gStat}
              onChange={setGStat}
              options={[
                { value: "kun-slag", label: "Kun slag" },
                { value: "slag-putter", label: "Slag og putter" },
                { value: "all", label: "All statistikk" },
              ]}
            />
          </div>

          <div className="flex flex-col gap-3 sm:flex-row-reverse">
            <Button
              type="button"
              size="lg"
              disabled={gFirstName.trim() === ""}
              onClick={() =>
                onAdd({
                  id: newId("guest"),
                  name: `${gFirstName.trim()} ${gLastName.trim()}`.trim(),
                  isGuest: true,
                  gender: gGender,
                  email: gEmail.trim() || undefined,
                  teeId: "",
                  hcp: gHcp,
                  statLevel: gStat,
                })
              }
              className="h-14 flex-1 rounded-2xl text-base font-bold"
            >
              Legg til gjest
            </Button>
            <Button type="button" variant="ghost" size="lg" onClick={() => setMode("search")} className="h-14 rounded-2xl px-6 text-base font-semibold">
              Tilbake til søk
            </Button>
          </div>
        </>
      )}
    </div>
  )
}

// --- Step 4: Lag og rekkefølge ------------------------------------------------

function Step4({
  format,
  players,
  setPlayers,
  sides,
  setSides,
}: {
  format: FormatId
  players: Player[]
  setPlayers: React.Dispatch<React.SetStateAction<Player[]>>
  sides: Side[]
  setSides: React.Dispatch<React.SetStateAction<Side[]>>
}) {
  if (format === "moneyball") {
    return (
      <div className="flex flex-col gap-6">
        <StepHeader title="Lag og rekkefølge" />
        <HelperNote>Tildel hver spiller en plass (0-3) -- avgjør hvem som er "money ball" på hvilket hull.</HelperNote>
        <ul className="flex flex-col gap-3">
          {players.map((p) => (
            <li key={p.id} className="flex flex-col gap-3 rounded-2xl border border-border bg-card p-4 shadow-md shadow-black/8">
              <div className="flex items-center gap-3">
                <Avatar name={p.name} />
                <span className="text-lg font-bold text-foreground">{p.name}</span>
              </div>
              <NumberButtons
                label={`Plass for ${p.name}`}
                values={[0, 1, 2, 3]}
                value={p.moneyBallSlot}
                onChange={(v) => setPlayers((prev) => prev.map((x) => (x.id === p.id ? { ...x, moneyBallSlot: v } : x)))}
              />
            </li>
          ))}
        </ul>
      </div>
    )
  }

  function createSide(label: string) {
    setSides((prev) => [...prev, { id: newId("side"), label: label.trim() || `Side ${prev.length === 0 ? "A" : "B"}` }])
  }
  function deleteSide(sideId: string) {
    setSides((prev) => prev.filter((s) => s.id !== sideId))
    setPlayers((prev) => prev.map((p) => (p.sideId === sideId ? { ...p, sideId: undefined } : p)))
  }
  function assign(playerId: string, sideId: string | undefined) {
    setPlayers((prev) => prev.map((p) => (p.id === playerId ? { ...p, sideId } : p)))
  }

  const unassigned = players.filter((p) => !p.sideId)

  return (
    <div className="flex flex-col gap-6">
      <StepHeader title="Lag og rekkefølge" description="Sett opp de to sidene." />

      <div className="grid gap-3 sm:grid-cols-2">
        {[0, 1].map((slot) => {
          const side = sides[slot]
          if (!side) {
            return <CreateSideCard key={slot} slot={slot} onCreate={createSide} disabled={sides.length < slot} />
          }
          const sidePlayers = players.filter((p) => p.sideId === side.id)
          const otherSide = sides.find((s) => s.id !== side.id)
          return (
            <div key={side.id} className="flex flex-col gap-3 rounded-2xl border-2 border-primary/40 bg-primary/5 p-4">
              <div className="flex items-center justify-between gap-2">
                <span className="text-lg font-bold text-foreground">{side.label}</span>
                <button type="button" onClick={() => deleteSide(side.id)} aria-label={`Slett ${side.label}`} className="flex size-10 items-center justify-center rounded-xl text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
                  <X aria-hidden="true" className="size-5" />
                </button>
              </div>
              {sidePlayers.length === 0 ? (
                <p className="text-base text-muted-foreground">Ingen spillere ennå.</p>
              ) : (
                <ul className="flex flex-col gap-2">
                  {sidePlayers.map((p) => (
                    <li key={p.id} className="flex items-center gap-2 rounded-xl border border-border bg-card p-2.5">
                      <Avatar name={p.name} />
                      <span className="flex-1 truncate text-base font-semibold text-foreground">{p.name}</span>
                      {otherSide && (
                        <button type="button" onClick={() => assign(p.id, otherSide.id)} className="min-h-[40px] rounded-lg px-2.5 text-sm font-bold text-primary hover:bg-primary/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
                          → annen side
                        </button>
                      )}
                      <button type="button" onClick={() => assign(p.id, undefined)} className="min-h-[40px] rounded-lg px-2.5 text-sm font-bold text-muted-foreground hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
                        Fjern fra side
                      </button>
                    </li>
                  ))}
                </ul>
              )}
            </div>
          )
        })}
      </div>

      {sides.length > 0 && unassigned.length > 0 && (
        <div className="flex flex-col gap-3">
          <SectionTitle title="Ikke tildelt" />
          <ul className="flex flex-col gap-2">
            {unassigned.map((p) => (
              <li key={p.id} className="flex items-center gap-3 rounded-2xl border border-border bg-card p-3 shadow-md shadow-black/8">
                <Avatar name={p.name} />
                <span className="flex-1 truncate text-base font-bold text-foreground">{p.name}</span>
                {sides.map((s, i) => (
                  <button key={s.id} type="button" onClick={() => assign(p.id, s.id)} className="min-h-[44px] rounded-xl border border-border px-3 text-sm font-bold text-foreground transition-colors hover:border-primary/50 hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
                    → {s.label || `Side ${i === 0 ? "A" : "B"}`}
                  </button>
                ))}
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  )
}

function CreateSideCard({ slot, onCreate, disabled }: { slot: number; onCreate: (label: string) => void; disabled: boolean }) {
  const [label, setLabel] = useState("")
  const fallback = slot === 0 ? "Side A" : "Side B"
  return (
    <div className={cn("flex flex-col gap-3 rounded-2xl border-2 border-dashed border-border bg-card/40 p-4", disabled && "opacity-50")}>
      <Label htmlFor={`side-label-${slot}`} className="text-base font-semibold">
        Navn på side (valgfritt)
      </Label>
      <Input id={`side-label-${slot}`} value={label} onChange={(e) => setLabel(e.target.value)} placeholder={slot === 0 ? "F.eks. Rødt lag" : "F.eks. Blått lag"} disabled={disabled} className="h-12 rounded-xl text-base" />
      <Button type="button" variant="outline" disabled={disabled} onClick={() => onCreate(label || fallback)} className="h-12 rounded-xl text-base font-bold">
        <Plus aria-hidden="true" className="mr-1.5 size-5" />
        Opprett side
      </Button>
    </div>
  )
}

// --- Step 5: Deling ------------------------------------------------------------

function Step5({
  visibility,
  setVisibility,
  categories,
  setCategories,
}: {
  visibility: Visibility
  setVisibility: (v: Visibility) => void
  categories: Set<string>
  setCategories: React.Dispatch<React.SetStateAction<Set<string>>>
}) {
  function toggleCategory(code: string) {
    setCategories((prev) => {
      const next = new Set(prev)
      if (next.has(code)) next.delete(code)
      else next.add(code)
      return next
    })
  }

  return (
    <div className="flex flex-col gap-6">
      <StepHeader title="Deling" />

      <div className="flex flex-col gap-3">
        <SectionTitle title="Synlighet" />
        <div className="flex flex-col gap-3">
          <ChoiceCard selected={visibility === "privat"} onClick={() => setVisibility("privat")} title="Privat" description="Kun deg og faktiske medspillere" icon={<Lock aria-hidden="true" className="size-5" />} />
          <ChoiceCard selected={visibility === "venner"} onClick={() => setVisibility("venner")} title="Venner" description="Valgte venne-kategorier kan følge live" icon={<Users aria-hidden="true" className="size-5" />} />
          <ChoiceCard selected={visibility === "offentlig"} onClick={() => setVisibility("offentlig")} title="Offentlig" description="Alle kan følge live, ingen innlogging" icon={<Globe aria-hidden="true" className="size-5" />} />
        </div>
      </div>

      {visibility === "venner" && (
        <div className="flex flex-col gap-3">
          <p className="text-base leading-relaxed text-muted-foreground text-pretty">
            Alle er forhåndsvalgt — velg heller bort det du ikke vil skal se den.
          </p>
          <div className="flex flex-wrap gap-2">
            {CATEGORY_OPTIONS.map(({ code, label }) => (
              <Pill key={code} selected={categories.has(code)} onClick={() => toggleCategory(code)}>
                {label}
              </Pill>
            ))}
          </div>
          {categories.size === 0 && (
            <p className="rounded-2xl border border-destructive/40 bg-destructive/10 px-4 py-3 text-base font-semibold text-destructive">
              Ingen kategori valgt ennå -- ingen venner kan se runden før du velger minst én.
            </p>
          )}
        </div>
      )}
    </div>
  )
}
