"use client";

import "leaflet/dist/leaflet.css";
import { useEffect, useRef } from "react";
import { MapContainer, TileLayer, useMap, useMapEvents } from "react-leaflet";
import { divIcon } from "leaflet";
import type { Map as LeafletMap } from "leaflet";
import { roundPoint, type LatLng } from "@/lib/gpx";
import { cn } from "@/lib/utils";

export const BASE_LAYERS = [
  {
    id: "osm",
    label: "OpenStreetMap",
    url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
    attribution: "&copy; OpenStreetMap contributors",
  },
  {
    id: "hot",
    label: "OpenStreetMap H.O.T.",
    url: "https://tile-{s}.openstreetmap.fr/hot/{z}/{x}/{y}.png",
    attribution: "&copy; OpenStreetMap contributors, H.O.T. style",
  },
];

function ClickCatcher({
  onPoint,
  onZoom,
}: {
  onPoint?: (point: LatLng) => void;
  onZoom?: (zoom: number) => void;
}) {
  useMapEvents({
    click(event) {
      onPoint?.(roundPoint(event.latlng.lat, event.latlng.lng));
    },
    zoomend(event) {
      onZoom?.(Math.round(event.target.getZoom()));
    },
  });
  return null;
}

export function MapShell({
  center,
  zoom,
  layerId,
  onPoint,
  onZoom,
  mapRef,
  className,
  children,
}: {
  center: LatLng;
  zoom: number;
  layerId: string;
  onPoint?: (point: LatLng) => void;
  onZoom?: (zoom: number) => void;
  mapRef?: (map: LeafletMap | null) => void;
  className?: string;
  children?: React.ReactNode;
}) {
  const layer = BASE_LAYERS.find((item) => item.id === layerId) ?? BASE_LAYERS[0];
  return (
    <div className="relative isolate">
      <MapContainer
        ref={mapRef}
        center={center}
        zoom={zoom}
        scrollWheelZoom={Boolean(onPoint)}
        className={cn("z-0 h-[420px] w-full", className)}
      >
        <TileLayer key={layer.id} url={layer.url} attribution={layer.attribution} />
        {(onPoint || onZoom) && <ClickCatcher onPoint={onPoint} onZoom={onZoom} />}
        {children}
      </MapContainer>
    </div>
  );
}

export function Recenter({
  point,
  zoom,
}: {
  point: LatLng;
  zoom: number;
}) {
  const map = useMap();
  useEffect(() => {
    map.setView(point, zoom);
    // Hanya saat koordinat tersimpan berubah; geseran manual pengguna tidak diganggu.
  }, [map, point[0], point[1], zoom]);
  return null;
}

export function FitOnLoad({ points, token }: { points: LatLng[]; token: string }) {
  const map = useMap();
  const titik = useRef(points);
  titik.current = points;
  useEffect(() => {
    if (titik.current.length > 1) map.fitBounds(titik.current, { padding: [24, 24] });
  }, [map, token]);
  return null;
}

export function pointIcon(color = "#0f766e") {
  const safe = /^#[0-9a-fA-F]{6}$/.test(color) ? color : "#0f766e";
  return divIcon({
    className: "",
    html: `<span style="display:block;width:20px;height:20px;border-radius:9999px;background:${safe};border:3px solid #fff;box-shadow:0 1px 6px rgba(15,23,42,.45)"></span>`,
    iconSize: [20, 20],
    iconAnchor: [10, 10],
  });
}

export function BaseLayerChoice({
  value,
  onChange,
}: {
  value: string;
  onChange: (id: string) => void;
}) {
  return (
    <div className="flex flex-wrap gap-3">
      {BASE_LAYERS.map((layer) => (
        <label
          key={layer.id}
          className="flex cursor-pointer items-center gap-2 text-sm text-foreground"
        >
          <input
            type="radio"
            name="map-tipe"
            value={layer.id}
            checked={value === layer.id}
            onChange={() => onChange(layer.id)}
            className="h-4 w-4 accent-[hsl(var(--primary))]"
          />
          {layer.label}
        </label>
      ))}
    </div>
  );
}

export function GpxUpload({
  onPoints,
  label = "Unggah GPX",
}: {
  onPoints: (points: LatLng[]) => void;
  label?: string;
}) {
  const inputRef = useRef<HTMLInputElement>(null);
  return (
    <>
      <input
        ref={inputRef}
        type="file"
        accept=".gpx,application/gpx+xml"
        className="hidden"
        onChange={async (event) => {
          const file = event.target.files?.[0];
          event.target.value = "";
          if (!file) return;
          const { parseGpx } = await import("@/lib/gpx");
          const points = parseGpx(await file.text());
          if (!points.length) {
            window.alert("Berkas GPX tidak memuat koordinat yang dapat dibaca.");
            return;
          }
          onPoints(points);
        }}
      />
      <button
        type="button"
        onClick={() => inputRef.current?.click()}
        className="inline-flex h-9 items-center gap-1.5 rounded-md border border-input bg-background px-3 text-sm font-medium hover:bg-muted"
      >
        {label}
      </button>
    </>
  );
}
