"use client"

import { useEffect, useRef, useState } from "react"
import Link from "next/link"
import { ArrowLeft, ChevronDown, ShieldAlert, Trophy } from "lucide-react"
import { Wordmark } from "@/components/wordmark"
import { cn } from "@/lib/utils"

// --- Typer (matcher app/routers/tournaments.py/matches.py/scoring.py) ------

type TeamStanding = {
  team_id: string
  team_name: string
  color: string | null
  points: number
  projected_points: number
}

type SessionStanding = {
  session_id: string
  sequence: number
  name: string | null
  points_by_team: Record<string, number>
  projected_points_by_team: Record<string, number>
  matches_total: number
  matches_decided: number
}

type Leaderboard = {
  teams: TeamStanding[]
  matches_total: number
  matches_decided: number
  sessions: SessionStanding[]
}

type ApiSession = {
  id: string
  sequence: number
  name: string | null
  format: string
  scoring_mode: string
  revealed: boolean
}

type ApiParticipant = { id: string; team_side: "a" | "b"; player_name: string; tee_name: string }

type ApiMatch = {
  id: string
  sequence: number
  team_a_id: string
  team_b_id: string
  status_text: string | null
  leading_side: "a" | "b" | null
  participants: ApiParticipant[]
}

type ApiScorecardHole = { hole_number: number; result: "a" | "b" | "halved" }

type ApiScorecard = {
  match_id: string
  status_text: string | null
  holes: ApiScorecardHole[]
}

const FORMAT_LABELS: Record<string, string> = {
  foursome: "Foursome",
  greensome: "Greensome",
  scramble_2: "Scramble (2)",
  scramble_4: "Scramble (4)",
  fourball: "Fourball",
  singles: "Singel",
}

function formatPoints(points: number) {
  return Number.isInteger(points) ? String(points) : points.toFixed(1).replace(".", ",")
}

function liveWsUrl(tournamentId: string, code?: string): string {
  const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"
  const query = code ? `?code=${encodeURIComponent(code)}` : ""
  return `${protocol}//${window.location.host}/ws/public/tournaments/${tournamentId}/live${query}`
}

export function PublicLive({ tournamentId, code }: { tournamentId: string; code?: string }) {
  const [loading, setLoading] = useState(true)
  const [accessError, setAccessError] = useState<string | null>(null)
  const [tournamentName, setTournamentName] = useState("")
  const [leaderboard, setLeaderboard] = useState<Leaderboard | null>(null)
  const [sessions, setSessions] = useState<ApiSession[]>([])
  const [refreshKey, setRefreshKey] = useState(0)
  const codeParam = code ? `?code=${encodeURIComponent(code)}` : ""

  // Sanntid (ADR-027): et rent "noe endret seg"-signal over WebSocket --
  // klienten reagerer ved å telle opp refreshKey, som utløser refetch her
  // OG i SessionRow/MatchRow under (kun for det som faktisk er åpent/synlig).
  useEffect(() => {
    const socket = new WebSocket(liveWsUrl(tournamentId, code))
    socket.onmessage = () => setRefreshKey((k) => k + 1)
    return () => socket.close()
  }, [tournamentId, code])

  const isFirstRefresh = useRef(true)
  useEffect(() => {
    if (isFirstRefresh.current) {
      isFirstRefresh.current = false
      return
    }
    fetch(`/public/tournaments/${tournamentId}/leaderboard${codeParam}`, { credentials: "include" })
      .then((res) => (res.ok ? res.json() : null))
      .then((lb: Leaderboard | null) => {
        if (lb) setLeaderboard(lb)
      })
      .catch(() => {})
  }, [refreshKey, tournamentId, codeParam])

  useEffect(() => {
    let cancelled = false
    async function load() {
      try {
        const infoRes = await fetch(`/public/tournaments/${tournamentId}${codeParam}`, { credentials: "include" })
        if (infoRes.status === 403 || infoRes.status === 404) {
          const body = await infoRes.json().catch(() => null)
          if (!cancelled) {
            setAccessError(
              body?.detail?.message ??
                (infoRes.status === 404 ? "Turneringen finnes ikke." : "Du har ikke tilgang til denne turneringen."),
            )
          }
          return
        }
        if (!infoRes.ok) throw new Error(`info: ${infoRes.status}`)
        const info: { name: string } = await infoRes.json()
        if (cancelled) return
        setTournamentName(info.name)

        const [lbRes, sessionsRes] = await Promise.all([
          fetch(`/public/tournaments/${tournamentId}/leaderboard${codeParam}`, { credentials: "include" }),
          fetch(`/public/tournaments/${tournamentId}/sessions${codeParam}`, { credentials: "include" }),
        ])
        if (lbRes.ok) {
          const lb: Leaderboard = await lbRes.json()
          if (!cancelled) setLeaderboard(lb)
        }
        if (sessionsRes.ok) {
          const s: ApiSession[] = await sessionsRes.json()
          if (!cancelled) setSessions(s.sort((a, b) => a.sequence - b.sequence))
        }
      } catch {
        if (!cancelled) setAccessError("Klarte ikke å laste stillingen. Prøv å laste siden på nytt.")
      } finally {
        if (!cancelled) setLoading(false)
      }
    }
    void load()
    return () => {
      cancelled = true
    }
  }, [tournamentId, codeParam])

  const backHref = `/t/${tournamentId}${code ? `?code=${encodeURIComponent(code)}` : ""}`

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

  if (accessError) {
    return (
      <div className="flex min-h-[100dvh] flex-col items-center justify-center gap-4 bg-background px-5 text-center">
        <div className="flex size-14 items-center justify-center rounded-2xl bg-muted">
          <ShieldAlert aria-hidden="true" className="size-7 text-muted-foreground" />
        </div>
        <p className="max-w-sm text-sm leading-relaxed text-muted-foreground text-pretty">{accessError}</p>
      </div>
    )
  }

  const teamsById = new Map(leaderboard?.teams.map((t) => [t.team_id, t]) ?? [])

  return (
    <div className="flex min-h-[100dvh] flex-col bg-background">
      <header className="sticky top-0 z-10 border-b border-border bg-background/80 backdrop-blur">
        <div className="mx-auto flex w-full max-w-2xl items-center gap-3 px-5 py-4">
          <Link
            href={backHref}
            aria-label="Tilbake til turneringssiden"
            className="flex size-10 shrink-0 items-center justify-center rounded-xl border border-border bg-card text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground"
          >
            <ArrowLeft aria-hidden="true" className="size-5" />
          </Link>
          <div className="flex min-w-0 flex-1 flex-col">
            <span className="flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
              <span aria-hidden="true" className="size-1.5 animate-pulse rounded-full bg-primary" />
              Følg live
            </span>
            <h1 className="truncate text-xl font-extrabold tracking-tight text-foreground">{tournamentName}</h1>
          </div>
          <Wordmark compact />
        </div>
      </header>

      <main className="mx-auto w-full max-w-2xl flex-1 px-5 py-6 sm:py-8">
        <div className="flex flex-col gap-6">
          {leaderboard && leaderboard.teams.length === 2 && <LeaderboardCard leaderboard={leaderboard} />}

          <section aria-label="Økter og matcher" className="flex flex-col gap-3">
            <h2 className="text-base font-bold text-foreground">Matcher</h2>
            {sessions.length === 0 && (
              <p className="text-sm text-muted-foreground">Ingen økter opprettet ennå.</p>
            )}
            {sessions.map((s) => (
              <SessionRow
                key={s.id}
                session={s}
                tournamentId={tournamentId}
                codeParam={codeParam}
                teamsById={teamsById}
                refreshKey={refreshKey}
              />
            ))}
          </section>
        </div>
      </main>
    </div>
  )
}

// --- Leaderboard -------------------------------------------------------------

function LeaderboardCard({ leaderboard }: { leaderboard: Leaderboard }) {
  const [a, b] = leaderboard.teams
  return (
    <section
      aria-label="Stilling"
      className="flex flex-col gap-4 rounded-3xl border border-border bg-card p-5 shadow-md shadow-black/8"
    >
      <div className="flex items-center gap-2">
        <Trophy aria-hidden="true" className="size-4 text-primary" />
        <h2 className="text-base font-bold text-foreground">Stilling</h2>
      </div>
      <StandingBar label="Nå" a={a} b={b} aValue={a.points} bValue={b.points} />
      <StandingBar label="Projisert (hvis pågående matcher holder seg)" a={a} b={b} aValue={a.projected_points} bValue={b.projected_points} />
      <p className="text-xs text-muted-foreground">
        {leaderboard.matches_decided} av {leaderboard.matches_total} matcher avgjort
      </p>
    </section>
  )
}

function StandingBar({
  label,
  a,
  b,
  aValue,
  bValue,
}: {
  label: string
  a: TeamStanding
  b: TeamStanding
  aValue: number
  bValue: number
}) {
  const total = aValue + bValue
  const aPct = total > 0 ? (aValue / total) * 100 : 50
  return (
    <div className="flex flex-col gap-1.5">
      <span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">{label}</span>
      <div className="flex items-center justify-between text-sm font-bold text-foreground">
        <span>{a.team_name} · {formatPoints(aValue)}</span>
        <span>{formatPoints(bValue)} · {b.team_name}</span>
      </div>
      <div className="flex h-3 w-full overflow-hidden rounded-full bg-muted">
        <div style={{ width: `${aPct}%`, backgroundColor: a.color ?? "#64748b" }} />
        <div style={{ width: `${100 - aPct}%`, backgroundColor: b.color ?? "#64748b" }} />
      </div>
    </div>
  )
}

// --- Session/match-liste -----------------------------------------------------

function SessionRow({
  session,
  tournamentId,
  codeParam,
  teamsById,
  refreshKey,
}: {
  session: ApiSession
  tournamentId: string
  codeParam: string
  teamsById: Map<string, TeamStanding>
  refreshKey: number
}) {
  const [open, setOpen] = useState(false)
  const [matches, setMatches] = useState<ApiMatch[] | null>(null)
  const [loadingMatches, setLoadingMatches] = useState(false)
  const title = session.name?.trim() || `Økt ${session.sequence}`

  async function fetchMatches() {
    setLoadingMatches(true)
    try {
      const res = await fetch(
        `/public/tournaments/${tournamentId}/sessions/${session.id}/matches${codeParam}`,
        { credentials: "include" },
      )
      if (res.ok) setMatches(await res.json())
    } finally {
      setLoadingMatches(false)
    }
  }

  function toggle() {
    const next = !open
    setOpen(next)
    if (next) void fetchMatches()
  }

  // Sanntid: hent matchene på nytt når WS sier noe endret seg -- men KUN
  // hvis økten faktisk er åpen (ingen vits i å hente noe brukeren ikke ser).
  const isFirstRefresh = useRef(true)
  useEffect(() => {
    if (isFirstRefresh.current) {
      isFirstRefresh.current = false
      return
    }
    if (open) void fetchMatches()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [refreshKey])

  return (
    <div className="overflow-hidden rounded-2xl border border-border bg-card">
      <button
        type="button"
        onClick={toggle}
        aria-expanded={open}
        className="flex w-full items-center justify-between gap-2 px-4 py-3.5 text-left"
      >
        <div className="flex flex-col">
          <span className="font-semibold text-foreground">{title}</span>
          <span className="text-xs text-muted-foreground">
            {FORMAT_LABELS[session.format] ?? session.format}
            {!session.revealed && " · ikke avslørt ennå"}
          </span>
        </div>
        <ChevronDown
          aria-hidden="true"
          className={cn("size-4 shrink-0 text-muted-foreground transition-transform", open && "rotate-180")}
        />
      </button>
      {open && (
        <div className="flex flex-col gap-2 border-t border-border p-3">
          {loadingMatches && (
            <div className="flex justify-center py-4">
              <div
                aria-hidden="true"
                className="size-5 animate-spin rounded-full border-2 border-primary/20 border-t-primary"
              />
            </div>
          )}
          {matches?.length === 0 && <p className="px-1 py-2 text-sm text-muted-foreground">Ingen matcher opprettet ennå.</p>}
          {matches?.map((m) => (
            <MatchRow
              key={m.id}
              match={m}
              tournamentId={tournamentId}
              codeParam={codeParam}
              teamA={teamsById.get(m.team_a_id)}
              teamB={teamsById.get(m.team_b_id)}
              refreshKey={refreshKey}
            />
          ))}
        </div>
      )}
    </div>
  )
}

function MatchRow({
  match,
  tournamentId,
  codeParam,
  teamA,
  teamB,
  refreshKey,
}: {
  match: ApiMatch
  tournamentId: string
  codeParam: string
  teamA?: TeamStanding
  teamB?: TeamStanding
  refreshKey: number
}) {
  const [showScorecard, setShowScorecard] = useState(false)
  const [scorecard, setScorecard] = useState<ApiScorecard | null>(null)
  const [loadingScorecard, setLoadingScorecard] = useState(false)
  const leadingColor = match.leading_side === "a" ? teamA?.color : match.leading_side === "b" ? teamB?.color : undefined

  async function fetchScorecard() {
    setLoadingScorecard(true)
    try {
      const res = await fetch(
        `/public/tournaments/${tournamentId}/matches/${match.id}/scorecard${codeParam}`,
        { credentials: "include" },
      )
      if (res.ok) setScorecard(await res.json())
    } finally {
      setLoadingScorecard(false)
    }
  }

  function toggleScorecard() {
    const next = !showScorecard
    setShowScorecard(next)
    if (next) void fetchScorecard()
  }

  const isFirstRefresh = useRef(true)
  useEffect(() => {
    if (isFirstRefresh.current) {
      isFirstRefresh.current = false
      return
    }
    if (showScorecard) void fetchScorecard()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [refreshKey])

  return (
    <div
      className="overflow-hidden rounded-xl border border-border"
      style={{ borderLeftWidth: 4, borderLeftColor: leadingColor ?? "transparent" }}
    >
      <button type="button" onClick={toggleScorecard} className="flex w-full flex-col gap-1.5 px-3 py-2.5 text-left">
        <div className="flex items-center justify-between gap-2">
          <span className="truncate text-sm font-semibold text-foreground">
            {teamA?.team_name ?? "Lag A"} vs. {teamB?.team_name ?? "Lag B"}
          </span>
          {match.status_text && (
            <span className="shrink-0 rounded-full bg-muted px-2 py-0.5 text-xs font-bold text-foreground">
              {match.status_text}
            </span>
          )}
        </div>
        {match.participants.length > 0 && (
          <span className="truncate text-xs text-muted-foreground">
            {match.participants.map((p) => p.player_name).join(" · ")}
          </span>
        )}
      </button>
      {showScorecard && (
        <div className="border-t border-border p-3">
          {loadingScorecard ? (
            <div className="flex justify-center py-3">
              <div
                aria-hidden="true"
                className="size-5 animate-spin rounded-full border-2 border-primary/20 border-t-primary"
              />
            </div>
          ) : scorecard && scorecard.holes.length > 0 ? (
            <ul className="flex flex-wrap gap-1.5">
              {scorecard.holes.map((h) => {
                const color = h.result === "a" ? teamA?.color : h.result === "b" ? teamB?.color : undefined
                return (
                  <li
                    key={h.hole_number}
                    title={`Hull ${h.hole_number}`}
                    className="flex size-8 items-center justify-center rounded-lg text-xs font-bold text-primary-foreground"
                    style={{ backgroundColor: color ?? "var(--muted)" }}
                  >
                    <span className={color ? "" : "text-muted-foreground"}>{h.hole_number}</span>
                  </li>
                )
              })}
            </ul>
          ) : (
            <p className="text-xs text-muted-foreground">Ingen hull registrert ennå.</p>
          )}
        </div>
      )}
    </div>
  )
}
