'use client';
import { useEffect, useRef, useState } from 'react';
import 'maplibre-gl/dist/maplibre-gl.css';

const SAT = '/api/tile/{z}/{x}/{y}';
export type Geo = { type: string; coordinates: number[][][] };
export type DeskParcel = { key: string; geometry: Geo; label?: string; tone?: 'main' | 'other'; href?: string };
type Pad = { top: number; bottom: number; left: number; right: number };
/** Yapı seçme kipi: OSM bina sınırları çizilir; parsel içine dokunulunca o yapı (yoksa nokta) seçilir.
 *  Çizim kipi (drawing): parsel içine her dokunuş satılan bölüme bir köşe ekler. */
export type Building = { center: [number, number]; area: number; ring: number[][] };
export type PickCfg = { active: boolean; value: [number, number] | null; onPick: (ll: [number, number]) => void; onBuildings?: (b: Building[]) => void;
  drawing?: boolean; ring?: [number, number][]; onVertex?: (ll: [number, number]) => void };
const OFM = 'https://tiles.openfreemap.org/planet';
function inRing([x, y]: [number, number], r: number[][]) {
  let c = false;
  for (let i = 0, j = r.length - 1; i < r.length; j = i++) { const [xi, yi] = r[i], [xj, yj] = r[j]; if ((yi > y) !== (yj > y) && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) c = !c; }
  return c;
}
function ringInfo(r: number[][]) {
  // alan ağırlıklı merkez + m² (yerel düzlem yaklaşımı)
  const lat = (r[0][1] * Math.PI) / 180, kx = 111320 * Math.cos(lat), ky = 110540;
  let A = 0, cx = 0, cy = 0;
  for (let i = 0; i < r.length - 1; i++) { const [x0, y0] = r[i], [x1, y1] = r[i + 1]; const f = x0 * y1 - x1 * y0; A += f; cx += (x0 + x1) * f; cy += (y0 + y1) * f; }
  const center: [number, number] = Math.abs(A) > 1e-14 ? [cx / (3 * A), cy / (3 * A)] : [r[0][0], r[0][1]];
  return { center, area: Math.abs(A / 2) * kx * ky };
}
/** Yapı alanının parsel içinde kalan oranı (8×8 nokta ızgarasıyla yaklaşık) */
function overlap(r: number[][], parcel: number[][]) {
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
  for (const [x, y] of r) { x0 = Math.min(x0, x); y0 = Math.min(y0, y); x1 = Math.max(x1, x); y1 = Math.max(y1, y); }
  let inB = 0, inBoth = 0;
  for (let i = 0; i < 8; i++) for (let j = 0; j < 8; j++) {
    const pt: [number, number] = [x0 + ((i + 0.5) / 8) * (x1 - x0), y0 + ((j + 0.5) / 8) * (y1 - y0)];
    if (inRing(pt, r)) { inB++; if (inRing(pt, parcel)) inBoth++; }
  }
  return inB ? inBoth / inB : 0;
}

function maxTileZoom() { return 19; }
const LAYERS = ['pf', 'pc', 'pl'];

/**
 * Harita masası: tam ekran HGM Atlas uydu görüntüsü + TKGM parsel sınırları.
 * "main" parseller turuncu, diğerleri beyaz çizilir; etiketler haritaya iğnelenir, tıklanınca href'e gider.
 * compare: ham uydu ile videodaki çizimi kaydırıcıyla karşılaştırma düğmesi gösterir.
 */
export function DeskMap({ parcels, pad, compare = false, onSelect, still = false, start, controls = true, poster, pick }: {
  pick?: PickCfg | null; poster?: string; controls?: boolean; parcels: DeskParcel[]; pad?: Partial<Pad>; compare?: boolean; onSelect?: (key: string) => void; still?: boolean; start?: { center: [number, number]; zoom: number };
}) {
  const el = useRef<HTMLDivElement>(null);
  const map = useRef<import('maplibre-gl').Map | null>(null);
  const ml = useRef<typeof import('maplibre-gl') | null>(null);
  const markers = useRef<import('maplibre-gl').Marker[]>([]);
  const [ready, setReady] = useState(false);
  const pickRef = useRef<PickCfg | null | undefined>(pick); pickRef.current = pick;
  const parcelsRef = useRef(parcels); parcelsRef.current = parcels;
  const bldRef = useRef<Building[]>([]); const pickMarker = useRef<import('maplibre-gl').Marker | null>(null);
  // poster: harita parsele odaklanıp karolar yüklenene kadar gösterilen hazır görüntü (boş koyu ekran yerine)
  const [painted, setPainted] = useState(!poster);
  useEffect(() => { if (painted) return; const t = setTimeout(() => setPainted(true), 8000); return () => clearTimeout(t); }, [painted]);
  const [before, setBefore] = useState<string | null>(null); const [pos, setPos] = useState(50);
  const padRef = useRef<Pad>({ top: 60, bottom: 60, left: 60, right: 60 });
  padRef.current = { top: 60, bottom: 60, left: 60, right: 60, ...pad };

  useEffect(() => {
    let off = false;
    import('maplibre-gl').then((mod) => {
      if (off || !el.current || map.current) return;
      ml.current = mod.default as unknown as typeof import("maplibre-gl");
      const m = new mod.default.Map({
        container: el.current, attributionControl: false, center: start?.center ?? [35.2, 39.0], zoom: start?.zoom ?? 5, preserveDrawingBuffer: true,
        pixelRatio: Math.min(window.devicePixelRatio || 1, 3), fadeDuration: 0, maxZoom: 18.3, interactive: !still, cooperativeGestures: false,
        style: { version: 8, sources: { sat: { type: 'raster', tiles: [SAT], tileSize: 256, maxzoom: 19 } }, layers: [{ id: 'sat', type: 'raster', source: 'sat', paint: { 'raster-resampling': 'linear', 'raster-fade-duration': 0 } }] },
      } as never);
      if (!still && controls) m.addControl(new mod.default.NavigationControl({ showCompass: false }), 'top-right');
      m.on('load', () => setReady(true));
      const ro = new ResizeObserver(() => m.resize()); ro.observe(el.current);
      m.once('remove', () => ro.disconnect());
      map.current = m;
    });
    return () => { off = true; };
  }, [still, start, controls]);

  useEffect(() => {
    const m = map.current, mod = ml.current; if (!m || !mod || !ready) return;
    setBefore(null);
    const data = { type: 'FeatureCollection' as const, features: parcels.map((p) => ({ type: 'Feature' as const, properties: { main: p.tone !== 'other' }, geometry: p.geometry as GeoJSON.Polygon })) };
    const src = m.getSource('p') as import('maplibre-gl').GeoJSONSource | undefined;
    if (src) src.setData(data);
    else {
      m.addSource('p', { type: 'geojson', data });
      m.addLayer({ id: 'pf', type: 'fill', source: 'p', paint: { 'fill-color': ['case', ['get', 'main'], '#FF6A1F', '#FFFFFF'], 'fill-opacity': ['case', ['get', 'main'], 0.22, 0.12] } });
      m.addLayer({ id: 'pc', type: 'line', source: 'p', paint: { 'line-color': '#000', 'line-width': 5, 'line-opacity': 0.35 } });
      m.addLayer({ id: 'pl', type: 'line', source: 'p', paint: { 'line-color': ['case', ['get', 'main'], '#FF6A1F', '#FFFFFF'], 'line-width': 2.6 } });
    }
    markers.current.forEach((k) => k.remove()); markers.current = [];
    for (const p of parcels) {
      if (!p.label) continue;
      const ring = p.geometry.coordinates[0];
      const top = ring.reduce((a, c) => (c[1] > a[1] ? c : a), ring[0]);
      const t = document.createElement(p.href || onSelect ? 'button' : 'div');
      t.className = `whitespace-nowrap px-1.5 py-0.5 text-[12px] font-semibold ${p.tone === 'other' ? 'bg-white text-ink' : 'bg-ink text-white'}`;
      t.textContent = p.label;
      if (p.href || onSelect) t.onclick = () => (onSelect ? onSelect(p.key) : (location.href = p.href!));
      markers.current.push(new mod.Marker({ element: t, anchor: 'bottom', offset: [0, -6] }).setLngLat(top as [number, number]).addTo(m));
    }
    if (!parcels.length) return;
    const xs = parcels.flatMap((p) => p.geometry.coordinates[0].map((c) => c[0])), ys = parcels.flatMap((p) => p.geometry.coordinates[0].map((c) => c[1]));
    const tz = maxTileZoom();
    {
      m.setMaxZoom(tz - 0.7);
      const w = m.getContainer().clientWidth, h = m.getContainer().clientHeight, P = { ...padRef.current };
      // Panel haritanın büyük kısmını kaplıyorsa (dar ekran) kenar boşluğunu küçült
      if (P.left + P.right > w * 0.8) { P.left = 30; P.right = 30; }
      if (P.top + P.bottom > h * 0.8) { P.top = 30; P.bottom = 30; }
      m.fitBounds([[Math.min(...xs), Math.min(...ys)], [Math.max(...xs), Math.max(...ys)]], { padding: P, duration: still ? 0 : 900, maxZoom: parcels.length > 1 ? 16.5 : tz - 0.7 });
      m.once('idle', () => setPainted(true));
    }
  }, [parcels, ready, onSelect, still]);

  // yapı seçme kipi: bina sınırları + parsel içindeki yapıların listesi
  const pickOn = !!pick;
  useEffect(() => {
    const m = map.current; if (!m || !ready || !pickOn) return;
    if (!m.getSource('ofm')) {
      m.addSource('ofm', { type: 'vector', url: OFM });
      m.addLayer({ id: 'bld', type: 'line', source: 'ofm', 'source-layer': 'building', minzoom: 14, paint: { 'line-color': '#FFFFFF', 'line-width': 1, 'line-opacity': 0.45 } }, m.getLayer('pf') ? 'pf' : undefined);
      m.addSource('bin', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } });
      m.addLayer({ id: 'bin-f', type: 'fill', source: 'bin', paint: { 'fill-color': ['case', ['get', 'sel'], '#FF6A1F', '#FFFFFF'], 'fill-opacity': ['case', ['get', 'sel'], 0.55, 0.22] } });
      m.addLayer({ id: 'bin-l', type: 'line', source: 'bin', paint: { 'line-color': ['case', ['get', 'sel'], '#FF6A1F', '#FFFFFF'], 'line-width': 2 } });
    }
    const scan = () => {
      const main = parcelsRef.current[0]; if (!main) return;
      const ring = main.geometry.coordinates[0]; const seen = new Map<string, Building>();
      for (const f of m.querySourceFeatures('ofm', { sourceLayer: 'building' })) {
        const polys = f.geometry.type === 'Polygon' ? [f.geometry.coordinates] : f.geometry.type === 'MultiPolygon' ? f.geometry.coordinates : [];
        for (const poly of polys) {
          const r = poly[0] as number[][]; if (!r || r.length < 4) continue;
          // yapının en az yarısı parselin içinde olmalı (merkezi tesadüfen içeri düşen L biçimli komşu yapılar sayılmaz)
          const info = ringInfo(r); if (info.area < 8 || overlap(r, ring) < 0.5) continue;
          // aynı yapı birden çok karoda parça parça gelir: OSM kimliğine göre en büyük parça tutulur
          const k = f.id != null ? `id${f.id}` : info.center.map((v) => v.toFixed(6)).join(',');
          const prev = seen.get(k); if (!prev || prev.area < info.area) seen.set(k, { ...info, ring: r });
        }
      }
      bldRef.current = [...seen.values()];
      draw(); pickRef.current?.onBuildings?.(bldRef.current);
    };
    const draw = () => {
      const v = pickRef.current?.value;
      (m.getSource('bin') as import('maplibre-gl').GeoJSONSource | undefined)?.setData({ type: 'FeatureCollection', features: bldRef.current.map((b) => ({ type: 'Feature' as const, properties: { sel: !!v && inRing(v, b.ring) }, geometry: { type: 'Polygon' as const, coordinates: [b.ring] } })) });
    };
    const onIdle = () => scan();
    const onClick = (e: import('maplibre-gl').MapMouseEvent) => {
      const pc = pickRef.current; const main = parcelsRef.current[0]; if (!pc || !main || (!pc.active && !pc.drawing)) return;
      const ll: [number, number] = [e.lngLat.lng, e.lngLat.lat];
      if (!inRing(ll, main.geometry.coordinates[0])) return; // parsel dışı seçilemez
      if (pc.drawing) { pc.onVertex?.(ll); return; }
      const b = bldRef.current.find((x) => inRing(ll, x.ring));
      pc.onPick(b ? b.center : ll);
    };
    m.on('idle', onIdle); m.on('click', onClick); scan();
    return () => { m.off('idle', onIdle); m.off('click', onClick); };
  }, [ready, pickOn]);
  // seçilen yapı işareti ve imleç
  useEffect(() => {
    const m = map.current, mod = ml.current; if (!m || !mod || !ready) return;
    m.getCanvas().style.cursor = pick?.active || pick?.drawing ? 'crosshair' : '';
    const v = pick?.value;
    if (!v) { pickMarker.current?.remove(); pickMarker.current = null; }
    else if (pickMarker.current) pickMarker.current.setLngLat(v);
    else {
      const el = document.createElement('div');
      el.innerHTML = '<svg width="30" height="38" viewBox="0 0 24 30" aria-hidden><path d="M12 29s-9-8.6-9-16a9 9 0 0 1 18 0c0 7.4-9 16-9 16z" fill="#FF6A1F" stroke="#111311" stroke-width="1.5"/><circle cx="12" cy="12" r="3.4" fill="#111311"/></svg>';
      pickMarker.current = new mod.Marker({ element: el, anchor: 'bottom' }).setLngLat(v).addTo(m);
    }
    const src = m.getSource('bin') as import('maplibre-gl').GeoJSONSource | undefined;
    if (src) src.setData({ type: 'FeatureCollection', features: bldRef.current.map((b) => ({ type: 'Feature' as const, properties: { sel: !!v && inRing(v, b.ring) }, geometry: { type: 'Polygon' as const, coordinates: [b.ring] } })) });
  }, [ready, pick?.active, pick?.value, pick?.drawing]);
  // satılan bölüm çizimi: köşeler, kenarlar, kapanınca dolgu
  useEffect(() => {
    const m = map.current; if (!m || !ready) return;
    const r = pick?.ring || [];
    const data = { type: 'FeatureCollection' as const, features: [
      ...(r.length >= 3 ? [{ type: 'Feature' as const, properties: {}, geometry: { type: 'Polygon' as const, coordinates: [[...r, r[0]]] } }] : []),
      ...(r.length >= 2 ? [{ type: 'Feature' as const, properties: {}, geometry: { type: 'LineString' as const, coordinates: pick?.drawing ? r : [...r, r[0]] } }] : []),
      ...r.map((p) => ({ type: 'Feature' as const, properties: {}, geometry: { type: 'Point' as const, coordinates: p } })),
    ] };
    const src = m.getSource('draw') as import('maplibre-gl').GeoJSONSource | undefined;
    if (src) { src.setData(data); return; }
    m.addSource('draw', { type: 'geojson', data });
    m.addLayer({ id: 'draw-f', type: 'fill', source: 'draw', filter: ['==', '$type', 'Polygon'], paint: { 'fill-color': '#FFD400', 'fill-opacity': 0.35 } });
    m.addLayer({ id: 'draw-l', type: 'line', source: 'draw', filter: ['==', '$type', 'LineString'], paint: { 'line-color': '#FFD400', 'line-width': 2.6 } });
    m.addLayer({ id: 'draw-v', type: 'circle', source: 'draw', filter: ['==', '$type', 'Point'], paint: { 'circle-radius': 4.5, 'circle-color': '#FFD400', 'circle-stroke-color': '#111311', 'circle-stroke-width': 1.5 } });
  }, [ready, pick?.ring, pick?.drawing]);

  function startCompare() {
    const m = map.current; if (!m) return;
    LAYERS.forEach((l) => m.setLayoutProperty(l, 'visibility', 'none')); markers.current.forEach((k) => (k.getElement().style.visibility = 'hidden'));
    m.once('idle', () => {
      setBefore(m.getCanvas().toDataURL('image/jpeg', 0.9));
      LAYERS.forEach((l) => m.setLayoutProperty(l, 'visibility', 'visible')); markers.current.forEach((k) => (k.getElement().style.visibility = ''));
      m.scrollZoom.disable(); m.dragPan.disable(); setPos(50);
    });
    m.triggerRepaint();
  }
  function endCompare() { const m = map.current; m?.scrollZoom.enable(); m?.dragPan.enable(); setBefore(null); }

  return (
    <div className="relative h-full w-full bg-[#2a2f28]">
      <div ref={el} className="h-full w-full" />
      {poster && <img src={poster} alt="" aria-hidden className={`pointer-events-none absolute inset-0 h-full w-full object-cover transition-opacity duration-500 ${painted ? 'opacity-0' : 'opacity-100'}`} />}
      {before && (
        <>
          <img src={before} alt="" className="pointer-events-none absolute inset-0 h-full w-full object-cover" style={{ clipPath: `inset(0 ${100 - pos}% 0 0)` }} />
          <div className="pointer-events-none absolute inset-y-0 w-0.5 bg-signal" style={{ left: `${pos}%` }} />
          <span className="pointer-events-none absolute top-14 bg-ink px-2 py-1 text-[12px] font-semibold text-white" style={{ left: `calc(${pos}% - 8px)`, transform: 'translateX(-100%)' }}>Önce: ham uydu</span>
          <span className="pointer-events-none absolute top-14 bg-signal px-2 py-1 text-[12px] font-semibold text-ink" style={{ left: `calc(${pos}% + 8px)` }}>Sonra: videodaki sınır</span>
          <input aria-label="Önce ve sonrayı karşılaştır" type="range" min={0} max={100} value={pos} onChange={(e) => setPos(+e.target.value)} className="absolute inset-0 h-full w-full cursor-ew-resize opacity-0" />
        </>
      )}
      {compare && ready && parcels.length > 0 && (
        <button type="button" data-compare onClick={before ? endCompare : startCompare} className="absolute right-[10px] top-[86px] bg-ink px-2.5 py-1.5 text-[12.5px] font-semibold text-white hover:bg-ink-2">{before ? 'Karşılaştırmayı kapat' : 'Önce / sonra'}</button>
      )}
      <span className="pointer-events-none absolute bottom-1 right-1.5 select-none text-[9px] leading-none text-white/45">parselstüdyo</span>
    </div>
  );
}
