"use client"

import { useEffect, useRef, useState } from "react"
import dynamic from "next/dynamic"
import { Crosshair, LocateFixed, MapPin, X, ChevronLeft, Check } from "lucide-react"
import { ClubPicker } from "@/components/teecup/club-picker"
import { Textarea } from "@/components/ui/textarea"
import { cn } from "@/lib/utils"
import { haversineMeters } from "@/lib/geo"

/**
 * Lazy, client-only reference to the map step. Because dynamic() defers the
 * underlying import() until <MapPointPicker /> is actually rendered, neither
 * this module nor a GPS-only run ever pulls in mapbox-gl. A Mapbox map load
 * (which is billed) only happens once the user chooses "Velg punkt på kart".
 */
const MapPointPicker = dynamic(
  () => import("@/components/shot/map-point-picker").then((m) => m.MapPointPicker),
  {
    ssr: false,
    loading: () => (
      <div className="flex h-full items-center justify-center">
        <div className="size-8 animate-spin rounded-full border-4 border-primary/20 border-t-primary" />
      </div>
    ),
  },
)

type LngLat = { lng: number; lat: number }
type Step = "start" | "map" | "end" | "club" | "result"
type GeoStatus = "idle" | "loading" | "error"

export type ShotMeasurementSheetProps = {
  holeNumber: number
  existingShotCount: number
  ownBagClubs: string[]
  /**
   * Kompassgrader (0-360) kartet roteres til, regnet ut av kalleren fra
   * spillerens EGET forrige slag på dette hullet (start→slutt-retningen) --
   * gir "opp på skjermen = fremover på hullet" uten lagrede green-
   * koordinater (brukerønske 2026-08-10). `undefined` når dette er det
   * første slaget som måles på hullet (ingen forrige slag å regne fra) --
   * MapPointPicker faller da selv tilbake til enhetens GPS-heading eller
   * nord, se den komponentens egen dokumentasjon.
   */
  initialBearing?: number
  /**
   * Computed by the caller (Haversine) once both points are known. Optional
   * here so the sheet stays previewable; when absent a rough preview-only
   * stub is used purely for display.
   */
  distanceMeters?: number
  /** Vises som en feilmelding i resultat-steget, f.eks. når serveren avviste
   *  innsendingen (ADR-048: reell bug 2026-08-08 -- uten dette lukket arket
   *  seg stille ved en avvist innsending, ingen feil synlig for brukeren). */
  submitError?: string | null
  submitting?: boolean
  onSubmit: (result: {
    club: string
    distanceMeters: number
    startMethod: "gps" | "map_tap"
    startLat: number
    startLng: number
    endMethod: "gps" | "map_tap"
    endLat: number
    endLng: number
    share: boolean
    shareText: string
  }) => void
  onCancel: () => void
}

// Preview-only rough distance so the result step shows a real number in v0.
// The real app passes distanceMeters computed server-/caller-side.
function previewDistance(a: LngLat | null, b: LngLat | null): number {
  if (!a || !b) return 0
  return Math.round(haversineMeters(a, b))
}

export function ShotMeasurementSheet({
  holeNumber,
  existingShotCount,
  ownBagClubs,
  initialBearing,
  distanceMeters,
  submitError,
  submitting,
  onSubmit,
  onCancel,
}: ShotMeasurementSheetProps) {
  const [step, setStep] = useState<Step>("start")
  const [startMethod, setStartMethod] = useState<"gps" | "map_tap" | null>(null)
  const [startPoint, setStartPoint] = useState<LngLat | null>(null)
  const [endMethod, setEndMethod] = useState<"gps" | "map_tap" | null>(null)
  const [endPoint, setEndPoint] = useState<LngLat | null>(null)

  const [startStatus, setStartStatus] = useState<GeoStatus>("idle")

  const [club, setClub] = useState<string>("")
  const [share, setShare] = useState(false)
  const [shareText, setShareText] = useState("")
  const shareEdited = useRef(false)

  function acquirePosition(onOk: (p: LngLat) => void, onFail: () => void) {
    if (typeof navigator === "undefined" || !navigator.geolocation) {
      onFail()
      return
    }
    navigator.geolocation.getCurrentPosition(
      (pos) => onOk({ lng: pos.coords.longitude, lat: pos.coords.latitude }),
      () => onFail(),
      { enableHighAccuracy: true, timeout: 10000 },
    )
  }

  // Start via device GPS.
  function useMyPosition() {
    setStartStatus("loading")
    acquirePosition(
      (p) => {
        setStartPoint(p)
        setStartMethod("gps")
        setStartStatus("idle")
        setStep("end")
      },
      () => setStartStatus("error"),
    )
  }

  const distance =
    typeof distanceMeters === "number" ? distanceMeters : previewDistance(startPoint, endPoint)

  // Forhåndsvisning av satellittutsnittet FØR lagring (brukerønske
  // 2026-08-08, ikke opprinnelig planlagt -- selve DELINGEN genererer sitt
  // eget, server-side bilde med samme markører+linje uavhengig av dette).
  // Bygget client-side med det OFFENTLIGE tokenet (trygt i nettleseren,
  // URL-restriktert) -- ingen server-tur-retur nødvendig kun for en
  // forhåndsvisning.
  const previewUrl =
    startPoint && endPoint && process.env.NEXT_PUBLIC_MAPBOX_TOKEN
      ? `https://api.mapbox.com/styles/v1/mapbox/satellite-streets-v12/static/pin-s-a+ff5a1f(${startPoint.lng},${startPoint.lat}),pin-s-b+2f7a3f(${endPoint.lng},${endPoint.lat})/auto/500x300@2x?padding=50&access_token=${process.env.NEXT_PUBLIC_MAPBOX_TOKEN}`
      : null

  // Prefill the (editable) share text once the result step is reached.
  useEffect(() => {
    if (step === "result" && !shareEdited.current) {
      setShareText(`⛳ ${club || "slag"}, ${distance} m – hull ${holeNumber}`)
    }
  }, [step, club, distance, holeNumber])

  function goBack() {
    if (step === "map" || step === "end") {
      setStartStatus("idle")
      setStep("start")
    } else if (step === "club") {
      // Re-measure the ball position rather than reusing a stale fix.
      setEndPoint(null)
      setEndMethod(null)
      setStep("end")
    } else if (step === "result") {
      setStep("club")
    }
  }

  function confirmSubmit() {
    if (!startPoint || !endPoint || !club) return
    onSubmit({
      club,
      distanceMeters: distance,
      startMethod: startMethod ?? "gps",
      startLat: startPoint.lat,
      startLng: startPoint.lng,
      endMethod: endMethod ?? "gps",
      endLat: endPoint.lat,
      endLng: endPoint.lng,
      share,
      shareText: share ? shareText : "",
    })
  }

  const onMapStep = step === "map" || step === "end"

  return (
    <div
      className="fixed inset-0 z-50 flex flex-col bg-background text-foreground"
      style={{ fontFamily: "var(--font-nunito), ui-sans-serif, system-ui, sans-serif" }}
      role="dialog"
      aria-modal="true"
      aria-label={`Mål et slag, hull ${holeNumber}`}
    >
      {/* Header */}
      <header className="flex shrink-0 items-start justify-between gap-3 border-b border-border px-4 pb-4 pt-[max(1rem,env(safe-area-inset-top))]">
        <div className="flex items-start gap-2">
          {step !== "start" ? (
            <button
              type="button"
              onClick={goBack}
              className="inline-flex min-h-11 items-center gap-1 rounded-xl px-2 text-base font-semibold text-muted-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
            >
              <ChevronLeft className="size-5 shrink-0" aria-hidden="true" />
              Tilbake
            </button>
          ) : null}
          <div className="pt-1.5">
            <h1 className="text-lg font-bold leading-tight text-balance">
              {`Mål et slag — hull ${holeNumber}`}
            </h1>
            {existingShotCount > 0 ? (
              <p className="mt-0.5 text-sm text-muted-foreground">
                {`${existingShotCount} slag målt tidligere`}
              </p>
            ) : null}
          </div>
        </div>

        <button
          type="button"
          onClick={onCancel}
          className="inline-flex min-h-11 items-center gap-1 rounded-xl px-3 text-base font-semibold text-muted-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
        >
          <X className="size-5 shrink-0" aria-hidden="true" />
          Lukk
        </button>
      </header>

      {/* Main */}
      <main className={cn("flex-1", onMapStep ? "overflow-hidden" : "overflow-y-auto p-4")}>
        {step === "start" ? (
          <div className="mx-auto flex max-w-md flex-col gap-4 pt-2">
            <p className="text-base text-muted-foreground text-pretty">
              Velg hvor slaget startet.
            </p>

            <button
              type="button"
              onClick={useMyPosition}
              disabled={startStatus === "loading"}
              className="flex min-h-16 items-center gap-4 rounded-2xl border border-border bg-card p-5 text-left transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-60"
            >
              <span className="flex size-11 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
                <LocateFixed className="size-6" aria-hidden="true" />
              </span>
              <span className="flex flex-col">
                <span className="text-base font-bold">Min posisjon nå</span>
                <span className="text-sm text-muted-foreground">Bruk telefonens GPS</span>
              </span>
            </button>

            <button
              type="button"
              onClick={() => {
                setStartMethod("map_tap")
                setStep("map")
              }}
              className="flex min-h-16 items-center gap-4 rounded-2xl border border-border bg-card p-5 text-left transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
            >
              <span className="flex size-11 shrink-0 items-center justify-center rounded-full bg-brand-orange/15 text-brand-orange">
                <MapPin className="size-6" aria-hidden="true" />
              </span>
              <span className="flex flex-col">
                <span className="text-base font-bold">Velg punkt på kart</span>
                <span className="text-sm text-muted-foreground">Trykk på satellittkartet</span>
              </span>
            </button>

            {startStatus === "loading" ? (
              <div className="flex items-center justify-center gap-3 pt-2">
                <div className="size-8 animate-spin rounded-full border-4 border-primary/20 border-t-primary" />
                <p className="text-base text-muted-foreground">Finner posisjonen din …</p>
              </div>
            ) : null}

            {startStatus === "error" ? (
              <div className="flex flex-col gap-3 rounded-2xl border border-border bg-card p-4">
                <p role="alert" className="text-base text-destructive text-pretty">
                  Fant ikke posisjonen din. Sjekk at posisjonstjenester er på, og prøv igjen.
                </p>
                <button
                  type="button"
                  onClick={useMyPosition}
                  className="inline-flex min-h-11 items-center justify-center gap-2 rounded-xl bg-primary px-4 text-base font-bold text-primary-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                >
                  <Crosshair className="size-5" aria-hidden="true" />
                  Prøv igjen
                </button>
              </div>
            ) : null}
          </div>
        ) : null}

        {/* method-argumentet i onConfirm ignoreres bevisst her -- alltid
            "map_tap" på start-steget siden ingen referencePoint er satt. */}
        {step === "map" ? (
          <MapPointPicker
            initialBearing={initialBearing}
            onConfirm={(p) => {
              setStartPoint(p)
              setStep("end")
            }}
          />
        ) : null}

        {/* Ball-steget viser NÅ alltid kartet (ADR-048-tillegg 2026-08-08,
            brukerønske: "jeg skal se start og slutt på et satelittfoto...
            mens jeg går opp mot ballen", "jeg ønsker å se lengden så langt
            i sanntid"). Ingen egen valg-skjerm lenger -- MapPointPicker
            selv viser BÅDE "Bekreft ballens posisjon" (trykket punkt) og
            "Jeg er ved ballen nå" (nåværende sporede posisjon) i footeren
            når referencePoint er satt, se den komponenten. */}
        {step === "end" ? (
          <MapPointPicker
            referencePoint={startPoint ?? undefined}
            referenceLabel="Utslag"
            initialBearing={initialBearing}
            onConfirm={(p, method) => {
              setEndPoint(p)
              setEndMethod(method)
              setStep("club")
            }}
          />
        ) : null}

        {step === "club" ? (
          <div className="mx-auto flex max-w-md flex-col gap-6 pt-2">
            <div className="flex flex-col gap-1">
              <h2 className="text-lg font-bold">Hvilken kølle brukte du?</h2>
              <p className="text-base text-muted-foreground">Velg køllen for dette slaget.</p>
            </div>
            {/* Reuses the shared ClubPicker (imported, not rebuilt). */}
            <ClubPicker value={club} onChange={setClub} ownBagClubs={ownBagClubs} />
          </div>
        ) : null}

        {step === "result" ? (
          <div className="mx-auto flex max-w-md flex-col gap-6 pt-2">
            {previewUrl ? (
              // eslint-disable-next-line @next/next/no-img-element -- ekstern Mapbox-URL, ikke next/image-verdt
              <img
                src={previewUrl || "/placeholder.svg"}
                alt="Satellittutsnitt av slaget, med start- og sluttpunkt markert"
                className="w-full rounded-2xl border border-border object-cover"
              />
            ) : null}
            <div className="flex flex-col items-center gap-1 rounded-2xl border border-border bg-card p-6">
              <span className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
                Målt lengde
              </span>
              <span className="text-6xl font-extrabold tabular-nums leading-none">
                {distance}
                <span className="ml-1 text-3xl font-bold text-muted-foreground">m</span>
              </span>
              {club ? (
                <span className="mt-2 inline-flex items-center rounded-full bg-muted px-3 py-1 text-sm font-semibold text-foreground">
                  {club}
                </span>
              ) : null}
            </div>

            {/* Private vs share segmented control */}
            <div className="flex flex-col gap-3">
              <span className="text-sm font-semibold text-muted-foreground">Deling</span>
              <div
                role="radiogroup"
                aria-label="Deling av slaget"
                className="grid grid-cols-2 gap-2 rounded-full bg-muted p-1"
              >
                {[
                  { key: false, label: "Behold privat" },
                  { key: true, label: "Del i feeden" },
                ].map((opt) => {
                  const selected = share === opt.key
                  return (
                    <button
                      key={String(opt.key)}
                      type="button"
                      role="radio"
                      aria-checked={selected}
                      onClick={() => setShare(opt.key)}
                      className={cn(
                        "inline-flex min-h-11 items-center justify-center rounded-full px-4 text-base font-bold transition-colors",
                        "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
                        selected
                          ? "bg-primary text-primary-foreground"
                          : "text-muted-foreground hover:text-foreground",
                      )}
                    >
                      {opt.label}
                    </button>
                  )
                })}
              </div>

              {share ? (
                <div className="flex flex-col gap-2">
                  <label htmlFor="share-text" className="text-sm font-semibold text-muted-foreground">
                    Innlegg
                  </label>
                  <Textarea
                    id="share-text"
                    value={shareText}
                    onChange={(e) => {
                      shareEdited.current = true
                      setShareText(e.target.value)
                    }}
                    rows={3}
                  />
                </div>
              ) : null}
            </div>

            {submitError ? (
              <p role="alert" className="text-base text-destructive text-pretty">
                {submitError}
              </p>
            ) : null}
          </div>
        ) : null}
      </main>

      {/* Footer — hidden on map step (MapPointPicker owns its own confirm bar)
          and on the transient GPS steps. */}
      {step === "club" || step === "result" ? (
        <footer className="shrink-0 border-t border-border bg-background p-4 pb-[max(1rem,env(safe-area-inset-bottom))]">
          {step === "club" ? (
            <button
              type="button"
              disabled={!club.trim()}
              onClick={() => setStep("result")}
              className="inline-flex min-h-14 w-full items-center justify-center rounded-xl bg-primary px-6 text-lg font-bold text-primary-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
            >
              Neste
            </button>
          ) : (
            <button
              type="button"
              disabled={submitting}
              onClick={confirmSubmit}
              className="inline-flex min-h-14 w-full items-center justify-center gap-2 rounded-xl bg-primary px-6 text-lg font-bold text-primary-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
            >
              {submitting ? (
                <div className="size-5 shrink-0 animate-spin rounded-full border-2 border-primary-foreground/30 border-t-primary-foreground" />
              ) : (
                <Check className="size-5 shrink-0" aria-hidden="true" />
              )}
              {submitting ? "Lagrer …" : "Lagre slag"}
            </button>
          )}
        </footer>
      ) : null}
    </div>
  )
}

export default ShotMeasurementSheet
