"use client";

// Sonuç sayfasının kahraman öğesi: 24 tercihin Türkiye haritası üzerinde
// risk renkli pinlerle gösterimi. Ülke seviyesinde il başına tek toplu pin;
// ile tıklayınca viewBox o ilin sınırlarına animasyonla akar, il büyür ve
// üniversite pinleri gerçek kampüs konumlarında adlarıyla görünür.
// Kilitli (paketsiz) tercihler il merkezinde adsız "?" pini olarak çizilir —
// üniversite adı client'a hiç inmediği için paywall haritadan delinemez.

import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import Image from "next/image";
import { ArrowLeft, Lock } from "lucide-react";
import { cities } from "turkey-map-react/lib/data";
import { Button } from "@/components/ui/button";
import { uniLogoYoluFromAd } from "@/components/uni-logo";
import {
  HARITA_GENISLIK,
  HARITA_UST_KIRPMA,
  HARITA_YUKSEKLIK,
  normalizeIlAdi,
} from "@/lib/harita";
import { RISK_PIN_RENK as RISK_RENK, type RiskSeviyesi } from "@/lib/risk";

export type HaritaPin = {
  /** Üniversite bazlı benzersiz anahtar (kilitlilerde sentetik) */
  id: string;
  /** Görünen ad — kilitli pinlerde boş bırakılır */
  universite: string;
  /** DB biçiminde il adı (büyük harf); harita dışıysa null */
  il: string | null;
  x: number;
  y: number;
  /** Bu üniversitedeki tercihlerin liste sıraları (1-24) */
  tercihSiralari: number[];
  /** tercihSiralari ile hizalı bölüm adları — kilitli pinlerde boş string */
  bolumler: string[];
  risk: RiskSeviyesi;
  kilitli?: boolean;
};

const TAM_GORUNUM = {
  x: 0,
  y: HARITA_UST_KIRPMA,
  w: HARITA_GENISLIK,
  h: HARITA_YUKSEKLIK - HARITA_UST_KIRPMA,
};

type ViewBox = typeof TAM_GORUNUM;

// normalizeIlAdi il başına modül yükünde bir kez — render başına 81 çağrı yerine
const NORMAL_SEHIRLER = cities.map((city) => ({
  city,
  key: normalizeIlAdi(city.name),
}));

/** "ORTA DOĞU TEKNİK ÜNİVERSİTESİ" → "Orta Doğu Teknik" */
function kisaUniAdi(ad: string): string {
  const kisa = ad
    .replace(/\s+(ÜNİVERSİTESİ|ENSTİTÜSÜ|MESLEK YÜKSEKOKULU)$/i, "")
    .trim();
  return kisa
    .toLocaleLowerCase("tr-TR")
    .split(" ")
    .map((k) =>
      k.length > 2 || k === "29"
        ? k.charAt(0).toLocaleUpperCase("tr-TR") + k.slice(1)
        : k,
    )
    .join(" ");
}

/** İldeki pinlerin çoğunluk riski (eşitlikte güvenli taraf). */
function baskinRisk(pinler: HaritaPin[]): RiskSeviyesi {
  const sayac: Record<RiskSeviyesi, number> = {
    guvenli: 0,
    "az-riskli": 0,
    riskli: 0,
  };
  for (const p of pinler) sayac[p.risk] += p.tercihSiralari.length;
  return (["guvenli", "az-riskli", "riskli"] as const).reduce((a, b) =>
    sayac[b] > sayac[a] ? b : a,
  );
}

/** Aynı noktaya düşen pinleri küçük bir halka üzerinde ayrıştırır. */
function cakismaAc(pinler: HaritaPin[], halka: number): HaritaPin[] {
  const gruplar = new Map<string, HaritaPin[]>();
  for (const p of pinler) {
    const key = `${Math.round(p.x)}:${Math.round(p.y)}`;
    const grup = gruplar.get(key);
    if (grup) grup.push(p);
    else gruplar.set(key, [p]);
  }
  const sonuc: HaritaPin[] = [];
  for (const grup of gruplar.values()) {
    if (grup.length === 1) {
      sonuc.push(grup[0]);
      continue;
    }
    grup.forEach((p, i) => {
      const aci = (2 * Math.PI * i) / grup.length;
      sonuc.push({
        ...p,
        x: p.x + Math.cos(aci) * halka,
        y: p.y + Math.sin(aci) * halka,
      });
    });
  }
  return sonuc;
}

export function TercihHaritasi({
  pinler,
  seciliIl,
  onSeciliIlDegisti,
  onTercihSec,
}: {
  pinler: HaritaPin[];
  /** Zoom'lanacak il (DB biçimi) — liste tarafından da kontrol edilebilir */
  seciliIl: string | null;
  onSeciliIlDegisti: (il: string | null) => void;
  /** Alt şeritte bir tercihe dokunulunca (liste satırına in) */
  onTercihSec?: (sira: number) => void;
}) {
  // 81 il path'inin inline ref callback'i her render'da yeniden üretilmesin
  // (React her render'da 81 detach/attach yapıyordu): key başına stabil
  // callback bir kez kurulur (il listesi statik). Ref yerine lazy-state
  // closure'ı — callback'ler commit anında koşar, render'da ref okunmaz.
  const [pathKayit] = useState(() => {
    const elemanlar = new Map<string, SVGPathElement>();
    const callbacks = new Map<string, (el: SVGPathElement | null) => void>();
    for (const { key } of NORMAL_SEHIRLER) {
      callbacks.set(key, (el) => {
        if (el) elemanlar.set(key, el);
        else elemanlar.delete(key);
      });
    }
    return { elemanlar, callbacks };
  });
  const svgRef = useRef<SVGSVGElement | null>(null);
  // vb yalnızca sf (zoom oran çarpanı) için state; viewBox akışı TRANSIENT —
  // kare başına setState 380ms boyunca tüm SVG ağacını (~23 render × 81 path)
  // yeniden kuruyordu. Artık animasyon svg.setAttribute ile ref üzerinden
  // akar, React yalnızca animasyon başında bir kez render eder.
  const [vb, setVb] = useState<ViewBox>(TAM_GORUNUM);
  const vbRef = useRef<ViewBox>(TAM_GORUNUM);
  const animRef = useRef(0);

  // viewBox JSX'te değil: React'in commit'i animasyon karesini ezmesin.
  // İlk değer mount'ta bir kez yazılır (stabil callback → yalnızca mount'ta).
  const svgMount = useCallback((el: SVGSVGElement | null) => {
    svgRef.current = el;
    if (el) {
      const v = vbRef.current;
      el.setAttribute("viewBox", `${v.x} ${v.y} ${v.w} ${v.h}`);
    }
  }, []);

  const animateTo = useCallback((hedef: ViewBox) => {
    cancelAnimationFrame(animRef.current);
    const kaynak = { ...vbRef.current };
    const t0 = performance.now();
    const SURE = 380;
    const ease = (t: number) => 1 - Math.pow(1 - t, 3);
    let ilkKare = true;
    const adim = (now: number) => {
      if (ilkKare) {
        ilkKare = false;
        // sf'e bağlı ölçüler tek render'da nihai değere oturur; rAF içinde
        // (asenkron) çağrıldığı için effect'te senkron setState sayılmaz.
        setVb(hedef);
      }
      const t = Math.min(1, (now - t0) / SURE);
      const k = ease(t);
      const sonraki = {
        x: kaynak.x + (hedef.x - kaynak.x) * k,
        y: kaynak.y + (hedef.y - kaynak.y) * k,
        w: kaynak.w + (hedef.w - kaynak.w) * k,
        h: kaynak.h + (hedef.h - kaynak.h) * k,
      };
      vbRef.current = sonraki;
      svgRef.current?.setAttribute(
        "viewBox",
        `${sonraki.x} ${sonraki.y} ${sonraki.w} ${sonraki.h}`,
      );
      if (t < 1) animRef.current = requestAnimationFrame(adim);
    };
    animRef.current = requestAnimationFrame(adim);
  }, []);

  useEffect(() => () => cancelAnimationFrame(animRef.current), []);

  // seciliIl değişince ilgili ilin bbox'ına (veya tam görünüme) ak
  useEffect(() => {
    if (!seciliIl) {
      animateTo(TAM_GORUNUM);
      return;
    }
    const path = pathKayit.elemanlar.get(seciliIl);
    if (!path) return;
    const b = path.getBBox();
    const pad = Math.max(b.width, b.height) * 0.22;
    let x = b.x - pad;
    let y = b.y - pad;
    let w = b.width + pad * 2;
    let h = b.height + pad * 2;
    // viewBox oranını koru ki il yamulmadan büyüsün ve harita yüksekliği
    // zoom'da değişmesin (tam görünümle aynı oran)
    const oran = TAM_GORUNUM.w / TAM_GORUNUM.h;
    if (w / h > oran) {
      const yeniH = w / oran;
      y -= (yeniH - h) / 2;
      h = yeniH;
    } else {
      const yeniW = h * oran;
      x -= (yeniW - w) / 2;
      w = yeniW;
    }
    animateTo({ x, y, w, h });
  }, [seciliIl, animateTo, pathKayit.elemanlar]);

  const ilPinleri = useMemo(() => {
    const gruplar = new Map<string, HaritaPin[]>();
    for (const p of pinler) {
      if (!p.il) continue;
      const grup = gruplar.get(p.il);
      if (grup) grup.push(p);
      else gruplar.set(p.il, [p]);
    }
    return gruplar;
  }, [pinler]);

  // Ekran boyutu sabit kalsın diye SVG içi ölçüler zoom oranıyla çarpılır
  const sf = vb.w / HARITA_GENISLIK;
  const zoomda = Boolean(seciliIl);
  const seciliPinler = useMemo(
    () => (seciliIl ? (ilPinleri.get(seciliIl) ?? []) : []),
    [ilPinleri, seciliIl],
  );
  // Halka yarıçapı zoom oranıyla ölçeklenir ki pinler ekranda hep aynı
  // mesafede ayrışsın (pin boyutları da sf ile ekran-sabit).
  const acikPinler = useMemo(
    () => cakismaAc(seciliPinler, 20 * sf),
    [seciliPinler, sf],
  );

  // Ülke seviyesi toplu pinler: il başına adet/merkez/renk tek geçişte
  // (üç ayrı reduce + baskinRisk dördüncü geçişti) ve memo'da.
  const ilOzetleri = useMemo(
    () =>
      [...ilPinleri.entries()].map(([il, grup]) => {
        let adet = 0;
        let cx = 0;
        let cy = 0;
        for (const p of grup) {
          adet += p.tercihSiralari.length;
          cx += p.x;
          cy += p.y;
        }
        return {
          il,
          adet,
          cx: cx / grup.length,
          cy: cy / grup.length,
          renk: RISK_RENK[baskinRisk(grup)],
        };
      }),
    [ilPinleri],
  );
  const ilAdetleri = useMemo(
    () => new Map(ilOzetleri.map((o) => [o.il, o.adet])),
    [ilOzetleri],
  );

  // Alt şerit: seçili ilin tercihleri sıraya göre (memo'suz flatMap+sort
  // her render'da yeniden kuruluyordu)
  const seritTercihleri = useMemo(
    () =>
      seciliPinler
        .flatMap((p) =>
          p.tercihSiralari.map((sira, i) => ({
            pin: p,
            sira,
            bolum: p.bolumler[i] ?? "",
          })),
        )
        .sort((a, b) => a.sira - b.sira),
    [seciliPinler],
  );

  return (
    <div>
      <div className="relative">
        <svg
          ref={svgMount}
          className="h-auto w-full"
          role="img"
          aria-label="Tercih listesinin Türkiye haritasındaki dağılımı"
        >
          {/* İl zeminleri */}
          {NORMAL_SEHIRLER.map(({ city, key }) => {
            const ilAdet = ilAdetleri.get(key);
            const iceriyor = ilAdet !== undefined;
            const secili = key === seciliIl;
            return (
              <path
                key={city.id}
                ref={pathKayit.callbacks.get(key)}
                d={city.path}
                className={
                  iceriyor
                    ? "cursor-pointer transition-opacity duration-200 hover:opacity-80"
                    : undefined
                }
                fill={
                  secili
                    ? "oklch(0.97 0.014 254.6)" // blue-50: seçili zemin
                    : iceriyor
                      ? "oklch(0.882 0.059 254.1)" // blue-200
                      : "oklch(0.929 0.013 255.5)" // slate-200
                }
                stroke="white"
                strokeWidth={secili ? 1.6 * sf : 1 * sf}
                onClick={() => {
                  if (secili) onSeciliIlDegisti(null);
                  else if (iceriyor) onSeciliIlDegisti(key);
                }}
              >
                <title>
                  {iceriyor
                    ? `${city.name}: ${ilAdet} tercih — yakınlaşmak için tıkla`
                    : city.name}
                </title>
              </path>
            );
          })}

          {/* Ülke seviyesi: il başına toplu pin */}
          {!zoomda
            ? ilOzetleri.map(({ il, adet, cx, cy, renk }) => (
                <g
                  key={il}
                  className="cursor-pointer"
                  onClick={() => onSeciliIlDegisti(il)}
                  role="button"
                  aria-label={`${il.toLocaleLowerCase("tr-TR")}: ${adet} tercih`}
                >
                  <circle
                    cx={cx}
                    cy={cy}
                    r={11}
                    fill={renk}
                    stroke="white"
                    strokeWidth={2}
                  />
                  <text
                    x={cx}
                    y={cy}
                    textAnchor="middle"
                    dominantBaseline="central"
                    fontSize={11}
                    fontWeight={700}
                    fill="white"
                    className="pointer-events-none select-none"
                  >
                    {adet}
                  </text>
                </g>
              ))
            : null}

          {/* İl seviyesi: üniversite pinleri */}
          {zoomda
            ? acikPinler.map((p) => {
                // Kilitli pinlerde logo da gösterilmez — üniversite kimliği
                // client'a inmediği gibi görselden de sızmamalı.
                const logo = p.kilitli ? null : uniLogoYoluFromAd(p.universite);
                const r = (p.kilitli ? 12 : logo ? 20 : 14) * sf;
                const etiket = p.kilitli ? null : kisaUniAdi(p.universite);
                return (
                  <g
                    key={p.id}
                    className={onTercihSec && !p.kilitli ? "cursor-pointer" : undefined}
                    onClick={
                      onTercihSec && !p.kilitli
                        ? () => onTercihSec(p.tercihSiralari[0])
                        : undefined
                    }
                    role={p.kilitli ? undefined : "button"}
                    aria-label={
                      p.kilitli
                        ? "Kilitli tercih"
                        : `${etiket}: ${p.tercihSiralari.length} tercih`
                    }
                  >
                    {logo ? (
                      <>
                        <circle
                          cx={p.x}
                          cy={p.y}
                          r={r}
                          fill="white"
                          stroke={RISK_RENK[p.risk]}
                          strokeWidth={2.5 * sf}
                        />
                        <image
                          href={logo}
                          x={p.x - 15 * sf}
                          y={p.y - 15 * sf}
                          width={30 * sf}
                          height={30 * sf}
                          preserveAspectRatio="xMidYMid meet"
                          className="pointer-events-none"
                        />
                        <circle
                          cx={p.x + r * 0.78}
                          cy={p.y - r * 0.78}
                          r={7.5 * sf}
                          fill={RISK_RENK[p.risk]}
                          stroke="white"
                          strokeWidth={1.5 * sf}
                        />
                        <text
                          x={p.x + r * 0.78}
                          y={p.y - r * 0.78}
                          textAnchor="middle"
                          dominantBaseline="central"
                          fontSize={9.5 * sf}
                          fontWeight={700}
                          fill="white"
                          className="pointer-events-none select-none"
                        >
                          {p.tercihSiralari.length}
                        </text>
                      </>
                    ) : (
                      <>
                        <circle
                          cx={p.x}
                          cy={p.y}
                          r={r}
                          fill={p.kilitli ? "oklch(0.704 0.04 256.8)" : RISK_RENK[p.risk]}
                          stroke="white"
                          strokeWidth={2 * sf}
                          strokeDasharray={p.kilitli ? `${4 * sf} ${3 * sf}` : undefined}
                        />
                        <text
                          x={p.x}
                          y={p.y}
                          textAnchor="middle"
                          dominantBaseline="central"
                          fontSize={13 * sf}
                          fontWeight={700}
                          fill="white"
                          className="pointer-events-none select-none"
                        >
                          {p.kilitli ? "?" : p.tercihSiralari.length}
                        </text>
                      </>
                    )}
                    {etiket ? (
                      <text
                        x={p.x}
                        y={p.y - r - 6 * sf}
                        textAnchor="middle"
                        fontSize={16 * sf}
                        fontWeight={600}
                        fill="oklch(0.279 0.041 260.0)" // slate-800
                        stroke="white"
                        strokeWidth={4 * sf}
                        paintOrder="stroke"
                        className="pointer-events-none select-none"
                      >
                        {etiket}
                      </text>
                    ) : null}
                  </g>
                );
              })
            : null}
        </svg>

        {zoomda ? (
          <Button
            size="sm"
            variant="outline"
            onClick={() => onSeciliIlDegisti(null)}
            className="absolute left-3 top-3 cursor-pointer rounded-full bg-white/90 backdrop-blur"
          >
            <ArrowLeft className="size-4" aria-hidden />
            Türkiye
          </Button>
        ) : null}
      </div>

      {/* Seçili ilin tercih şeridi (yalnızca zoom'dayken) */}
      {zoomda && seciliPinler.length > 0 ? (
        <div className="mt-2">
          <ul
            aria-label={`${seciliIl!.toLocaleUpperCase("tr-TR")} tercihlerin`}
            className="flex gap-2 overflow-x-auto pb-1"
          >
            {seritTercihleri.map(({ pin, sira, bolum }) => {
              const logo = pin.kilitli ? null : uniLogoYoluFromAd(pin.universite);
              return (
                <li key={sira} className="shrink-0">
                  <button
                    type="button"
                    onClick={onTercihSec ? () => onTercihSec(sira) : undefined}
                    className="flex cursor-pointer items-center gap-2 rounded-full border border-slate-200 bg-white py-1.5 pl-1.5 pr-3 text-xs transition-colors duration-200 hover:border-slate-300 hover:bg-slate-50"
                  >
                    <span
                      className="flex size-5 items-center justify-center rounded-full font-heading text-[10px] font-bold text-white"
                      style={{ backgroundColor: RISK_RENK[pin.risk] }}
                    >
                      {sira}
                    </span>
                    {pin.kilitli ? (
                      <span className="inline-flex items-center gap-1 text-slate-400">
                        <Lock className="size-3" aria-hidden />
                        Kilitli tercih
                      </span>
                    ) : (
                      <span className="inline-flex min-w-0 items-center gap-2">
                        {logo ? (
                          <Image
                            src={logo}
                            alt=""
                            width={20}
                            height={20}
                            aria-hidden
                            className="size-5 shrink-0 object-contain"
                          />
                        ) : null}
                        <span className="flex min-w-0 flex-col items-start leading-tight">
                          <span className="max-w-48 truncate font-medium text-slate-700">
                            {bolum || kisaUniAdi(pin.universite)}
                          </span>
                          {bolum ? (
                            <span className="max-w-48 truncate text-[10px] text-slate-500">
                              {kisaUniAdi(pin.universite)}
                            </span>
                          ) : null}
                        </span>
                      </span>
                    )}
                  </button>
                </li>
              );
            })}
          </ul>
        </div>
      ) : null}
    </div>
  );
}
