"use client";

import dynamic from "next/dynamic";
import Image from "next/image";
import { useMemo, useRef, useState } from "react";
import { MapPin, Maximize, Minus, Plus, Search } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import type { LocationDirectoryRecord } from "./api-contract";
import { locationDirectoryAddress } from "./model";
import {
  indonesiaMapPosition,
  isLocationOnIndonesiaMap,
  type GoogleMapConfig,
} from "./location-map-model";

type MappedLocation = LocationDirectoryRecord & {
  latitude: number;
  longitude: number;
};

const GoogleLocationCanvas = dynamic(
  () =>
    import("./google-location-canvas").then(
      (module) => module.GoogleLocationCanvas,
    ),
  {
    ssr: false,
    loading: () => (
      <div
        role="status"
        className="absolute inset-0 flex items-center justify-center bg-muted text-sm text-muted-foreground"
      >
        Loading Google Maps…
      </div>
    ),
  },
);

function searchableLocation(row: LocationDirectoryRecord) {
  return [row.name, row.code, row.city, row.address.street_and_number]
    .join(" ")
    .toLowerCase();
}

export function LocationMap({
  rows,
  selectedId,
  onSelect,
  mapConfig,
}: {
  rows: readonly LocationDirectoryRecord[];
  selectedId: string;
  onSelect: (id: string) => void;
  mapConfig: GoogleMapConfig | null;
}) {
  const [search, setSearch] = useState("");
  const [zoom, setZoom] = useState(1);
  const [offset, setOffset] = useState({ x: 0, y: 0 });
  const [notice, setNotice] = useState("");
  const [forceOffline, setForceOffline] = useState(false);
  const container = useRef<HTMLDivElement>(null);
  const drag = useRef<{ x: number; y: number; left: number; top: number } | null>(null);
  const query = search.trim().toLowerCase();
  const filteredRows = useMemo(
    () => rows.filter((row) => !query || searchableLocation(row).includes(query)),
    [rows, query],
  );
  const googleRows = useMemo(
    () =>
      filteredRows.filter(
        (row): row is MappedLocation =>
          row.latitude !== null &&
          row.longitude !== null &&
          Number.isFinite(row.latitude) &&
          Number.isFinite(row.longitude),
      ),
    [filteredRows],
  );
  const offlineRows = useMemo(
    () => googleRows.filter(isLocationOnIndonesiaMap),
    [googleRows],
  );
  const offlineMode = !mapConfig || forceOffline;
  const mappedRows = offlineMode ? offlineRows : googleRows;
  const mappedIds = new Set(mappedRows.map((row) => row.id));
  const groups = useMemo(() => {
    const grouped = new Map<string, MappedLocation[]>();
    offlineRows.forEach((row) => {
      const key = row.latitude.toFixed(2) + ":" + row.longitude.toFixed(2);
      grouped.set(key, [...(grouped.get(key) ?? []), row]);
    });
    return [...grouped.entries()];
  }, [offlineRows]);

  async function toggleFullscreen() {
    try {
      if (document.fullscreenElement) await document.exitFullscreen();
      else await container.current?.requestFullscreen();
    } catch {
      setNotice("Fullscreen is unavailable in this browser.");
    }
  }

  function switchToOfflineMap() {
    setForceOffline(true);
    setNotice("Google Maps could not load. Showing the offline map.");
  }

  return (
    <section className="panel overflow-hidden">
      <div className="flex flex-wrap items-center justify-between gap-4 border-b p-5">
        <div>
          <h2 className="text-lg font-semibold">Locations on the map</h2>
          <p className="mt-1 text-sm text-muted-foreground">
            {mappedRows.length} mapped · {rows.length} locations · Select a marker or search by name, code, address, or city.
          </p>
        </div>
        <div className="relative w-full sm:w-72">
          <Search
            aria-hidden="true"
            className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground"
          />
          <Input
            aria-label="Search locations on map"
            placeholder="Search locations…"
            value={search}
            onChange={(event) => setSearch(event.target.value)}
            className="pl-10"
          />
        </div>
      </div>
      {!mapConfig && (
        <p role="status" className="border-b px-5 py-3 text-sm text-muted-foreground">
          Google Maps configuration is unavailable. Showing the offline map.
        </p>
      )}

      <div className="grid min-w-0 lg:grid-cols-[300px_minmax(0,1fr)]">
        <aside
          aria-label="Locations shown on map"
          className="max-h-72 overflow-y-auto border-b p-3 lg:max-h-[560px] lg:border-b-0 lg:border-r"
        >
          <div className="flex items-center justify-between px-2 pb-2 pt-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">
            <span>Location directory</span>
            <span>{filteredRows.length}</span>
          </div>
          {filteredRows.length ? (
            filteredRows.map((row) => {
              const active = selectedId === row.id;
              const mapped = mappedIds.has(row.id);
              return (
                <button
                  key={row.id}
                  type="button"
                  aria-pressed={active}
                  onClick={() => onSelect(row.id)}
                  className={
                    "mb-2 flex w-full items-start gap-3 rounded-lg border p-3 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring " +
                    (active
                      ? "border-primary/40 bg-accent/50"
                      : "border-transparent hover:border-border hover:bg-muted/70")
                  }
                >
                  <MapPin
                    aria-hidden="true"
                    className={
                      "mt-0.5 size-4 shrink-0 " +
                      (mapped ? "text-primary" : "text-muted-foreground")
                    }
                  />
                  <span className="min-w-0 flex-1">
                    <span className="block truncate font-medium">{row.name}</span>
                    <span className="mt-1 block truncate text-xs text-muted-foreground">
                      {locationDirectoryAddress(row)}
                    </span>
                    <span className="mt-2 block truncate text-xs text-muted-foreground">
                      {row.code} · {row.active ? "Active" : "Inactive"}
                      {!mapped ? " · Not mapped" : ""}
                    </span>
                  </span>
                </button>
              );
            })
          ) : (
            <p role="status" className="p-4 text-sm text-muted-foreground">
              No locations match “{search}”.
            </p>
          )}
        </aside>

        <div
          ref={container}
          className={
            "relative h-[420px] min-w-0 overflow-hidden bg-muted fullscreen:h-screen fullscreen:w-screen fullscreen:rounded-none lg:h-[560px] " +
            (offlineMode ? "cursor-grab active:cursor-grabbing" : "")
          }
        >
          {mapConfig && !offlineMode ? (
            <GoogleLocationCanvas
              points={googleRows}
              selectedId={selectedId}
              config={mapConfig}
              onSelect={onSelect}
              onUseOfflineMap={switchToOfflineMap}
            />
          ) : (
            <div
              className="absolute inset-0 touch-none"
              onPointerDown={(event) => {
                if (event.target instanceof HTMLElement && event.target.closest("button, input, a")) return;
                drag.current = {
                  x: event.clientX,
                  y: event.clientY,
                  left: offset.x,
                  top: offset.y,
                };
                event.currentTarget.setPointerCapture(event.pointerId);
              }}
              onPointerMove={(event) => {
                if (!drag.current) return;
                setOffset({
                  x: drag.current.left + event.clientX - drag.current.x,
                  y: drag.current.top + event.clientY - drag.current.y,
                });
              }}
              onPointerUp={() => { drag.current = null; }}
              onPointerCancel={() => { drag.current = null; }}
            >
              <div
                className="absolute inset-0"
                style={{
                  transform: "translate(" + offset.x + "px, " + offset.y + "px) scale(" + zoom + ")",
                  transformOrigin: "50% 50%",
                }}
              >
                <Image
                  src="/images/map/indonesia.svg"
                  alt="Offline map of Indonesia"
                  fill
                  loading="eager"
                  unoptimized
                  draggable={false}
                  className="select-none object-fill dark:brightness-[0.72] dark:saturate-[0.7]"
                />
                {groups.map(([key, group]) => {
                  const point = group[0];
                  const position = indonesiaMapPosition(point);
                  const isSelected = group.some((item) => item.id === selectedId);
                  return (
                    <button
                      key={key}
                      type="button"
                      aria-label={
                        group.length > 1
                          ? "Select " + group.length + " locations near " + point.name
                          : "Select " + point.name + " on map"
                      }
                      aria-pressed={isSelected}
                      onClick={() => onSelect(point.id)}
                      className={
                        "absolute z-10 flex size-10 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full border-4 text-xs font-semibold text-white shadow-md transition-colors " +
                        (isSelected
                          ? "border-primary/25 bg-primary"
                          : "border-info/25 bg-info hover:bg-primary")
                      }
                      style={{
                        left: position.x + "%",
                        top: position.y + "%",
                        transform: "translate(-50%, -50%) scale(" + (1 / zoom) + ")",
                      }}
                    >
                      {group.length > 1 ? group.length : <MapPin aria-hidden="true" className="size-5" />}
                    </button>
                  );
                })}
              </div>

              {!mappedRows.length && (
                <div className="absolute inset-x-4 top-4 z-10 rounded-lg border bg-card/95 p-3 text-sm text-muted-foreground shadow-sm sm:right-auto sm:max-w-sm">
                  {filteredRows.length
                    ? "No matching location has coordinates within the Indonesia map."
                    : "No locations match this search."}
                </div>
              )}
              <div className="absolute bottom-9 right-3 z-20 flex flex-col rounded-lg bg-card shadow-sm">
                <Button
                  variant="ghost"
                  size="icon"
                  aria-label="Zoom in"
                  onClick={() => setZoom((current) => Math.min(5, current + 0.5))}
                  disabled={zoom >= 5}
                >
                  <Plus aria-hidden="true" />
                </Button>
                <Button
                  variant="ghost"
                  size="icon"
                  aria-label="Zoom out"
                  onClick={() => setZoom((current) => Math.max(1, current - 0.5))}
                  disabled={zoom <= 1}
                >
                  <Minus aria-hidden="true" />
                </Button>
              </div>
              <p
                role="status"
                className="absolute inset-x-0 bottom-0 z-20 bg-card/90 px-3 py-1.5 text-xs text-muted-foreground"
              >
                {notice || "Offline Indonesia map · Markers use location coordinates from TIAR"}
              </p>
            </div>
          )}
          {!offlineMode && !mappedRows.length && (
            <div className="pointer-events-none absolute inset-x-4 top-4 z-10 rounded-lg border bg-card/95 p-3 text-sm text-muted-foreground shadow-sm sm:right-auto sm:max-w-sm">
              {filteredRows.length
                ? "No matching location has coordinates to show on the map."
                : "No locations match this search."}
            </div>
          )}
          <Button
            variant="outline"
            size="icon"
            aria-label="Toggle fullscreen map"
            className="absolute right-3 top-3 z-20 bg-card shadow-sm"
            onClick={toggleFullscreen}
          >
            <Maximize aria-hidden="true" />
          </Button>
        </div>
      </div>
    </section>
  );
}
