"use client";

import { useEffect, useRef } from "react";
import mapboxgl from "mapbox-gl";
import MapboxDraw from "@mapbox/mapbox-gl-draw";
import "mapbox-gl/dist/mapbox-gl.css";
import "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css";
import { LatLng, Zone, ZoneType } from "@/lib/api";
import { ZONE_COLORS } from "@/components/ZoneMap";

const RIYADH: [number, number] = [46.6753, 24.7136];

/**
 * Mapbox GL implementation of the zones map: renders each zone as a coloured polygon and draws new
 * ones with mapbox-gl-draw. Same contract as the Google `ZoneMap` (a finished polygon is handed up
 * as plain lat/lng points), so the page logic is provider-agnostic.
 */
export function MapboxZoneMap({
  token,
  zones,
  selectedId,
  drawing,
  onDrawn,
  onSelect,
  className = "h-[520px] w-full overflow-hidden rounded-card border border-line",
}: {
  token: string;
  zones: Zone[];
  selectedId: string | null;
  drawing: boolean;
  onDrawn: (polygon: LatLng[]) => void;
  onSelect: (id: string) => void;
  className?: string;
}) {
  const ref = useRef<HTMLDivElement>(null);
  const mapRef = useRef<mapboxgl.Map | null>(null);
  const drawRef = useRef<MapboxDraw | null>(null);
  const readyRef = useRef(false);

  // --- one-time map init ---------------------------------------------------
  useEffect(() => {
    if (!ref.current || mapRef.current) return;
    mapboxgl.accessToken = token;
    const map = new mapboxgl.Map({
      container: ref.current,
      style: "mapbox://styles/mapbox/dark-v11",
      center: RIYADH,
      zoom: 11,
      attributionControl: false,
    });
    map.addControl(new mapboxgl.NavigationControl({ showCompass: false }), "top-right");
    mapRef.current = map;

    map.on("load", () => {
      readyRef.current = true;
      map.addSource("zones", { type: "geojson", data: emptyFC() });
      map.addLayer({
        id: "zones-fill",
        type: "fill",
        source: "zones",
        paint: {
          "fill-color": ["get", "color"],
          "fill-opacity": ["case", ["get", "selected"], 0.35, ["get", "active"], 0.18, 0.06],
        },
      });
      map.addLayer({
        id: "zones-line",
        type: "line",
        source: "zones",
        paint: {
          "line-color": ["get", "color"],
          "line-width": ["case", ["get", "selected"], 4, 2],
          "line-opacity": ["case", ["get", "active"], 0.9, 0.35],
        },
      });
      map.on("click", "zones-fill", (e) => {
        const id = e.features?.[0]?.properties?.id;
        if (id) onSelect(id as string);
      });
      map.on("mouseenter", "zones-fill", () => (map.getCanvas().style.cursor = "pointer"));
      map.on("mouseleave", "zones-fill", () => (map.getCanvas().style.cursor = ""));
      syncZones();
    });

    return () => {
      map.remove();
      mapRef.current = null;
      readyRef.current = false;
    };
    // token is stable for a mounted map; re-init on change is unnecessary.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [token]);

  // --- render zones --------------------------------------------------------
  const syncZones = () => {
    const map = mapRef.current;
    if (!map || !readyRef.current) return;
    const src = map.getSource("zones") as mapboxgl.GeoJSONSource | undefined;
    src?.setData({
      type: "FeatureCollection",
      features: zones.map((z) => ({
        type: "Feature",
        properties: {
          id: z.id,
          color: ZONE_COLORS[z.type as ZoneType],
          active: z.active,
          selected: z.id === selectedId,
        },
        geometry: {
          type: "Polygon",
          // GeoJSON is [lng, lat] and rings must close.
          coordinates: [closeRing(z.polygon.map((p) => [p.lng, p.lat]))],
        },
      })),
    });
  };
  useEffect(syncZones, [zones, selectedId]);

  // --- drawing mode --------------------------------------------------------
  useEffect(() => {
    const map = mapRef.current;
    if (!map || !readyRef.current) return;

    if (!drawing) {
      if (drawRef.current) {
        map.removeControl(drawRef.current);
        drawRef.current = null;
      }
      return;
    }

    const draw = new MapboxDraw({
      displayControlsDefault: false,
      defaultMode: "draw_polygon",
    });
    map.addControl(draw);
    drawRef.current = draw;

    const onCreate = (e: { features: GeoJSON.Feature[] }) => {
      const geom = e.features[0]?.geometry;
      if (geom?.type === "Polygon") {
        const ring = geom.coordinates[0] ?? [];
        // Drop the closing point mapbox-gl-draw appends; hand up as {lat,lng}.
        const points: LatLng[] = ring
          .slice(0, -1)
          .map(([lng, lat]) => ({ lat, lng }));
        draw.deleteAll();
        onDrawn(points);
      }
    };
    map.on("draw.create", onCreate);
    return () => {
      map.off("draw.create", onCreate);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [drawing]);

  return (
    <div ref={ref} className={className} />
  );
}

function emptyFC(): GeoJSON.FeatureCollection {
  return { type: "FeatureCollection", features: [] };
}

/** Ensure a polygon ring is closed (first point repeated at the end). */
function closeRing(coords: number[][]): number[][] {
  if (coords.length === 0) return coords;
  const [fx, fy] = coords[0];
  const [lx, ly] = coords[coords.length - 1];
  return fx === lx && fy === ly ? coords : [...coords, coords[0]];
}
