// Simple map hook for displaying a single station on the info page

import { isValidCoordinate } from "../map_helpers";

declare const L: typeof import("leaflet");

// ~1.1 m at the equator — enough to ignore float noise but still notice any
// real position change from the server.
const POSITION_EPSILON = 0.00001;

interface InfoMapContext {
  el: HTMLElement;
  map: import("leaflet").Map | null;
  marker: import("leaflet").Marker | null;
  lastSymbolHtml: string | null;
  initializing: boolean;
  resizeTimer: ReturnType<typeof setTimeout> | null;
  destroyed?: boolean;
}

export const InfoMap = {
  mounted(this: InfoMapContext) {
    this.map = null;
    this.marker = null;
    this.lastSymbolHtml = null;
    this.initializing = false;
    this.resizeTimer = null;
    this.destroyed = false;
    initializeMap.call(this);
  },

  updated(this: InfoMapContext) {
    const lat = parseFloat(this.el.dataset.lat || "");
    const lon = parseFloat(this.el.dataset.lon || "");
    const symbolHtml = this.el.dataset.symbolHtml || null;
    const callsign = this.el.dataset.callsign || "unknown";

    if (!isValidCoordinate(lat, lon)) {
      console.warn(
        `InfoMap: Invalid coordinates lat=${lat}, lon=${lon} for ${callsign} during update`,
      );
      return;
    }

    if (
      !this.map ||
      !(this.map as any)._container ||
      !(this.map as any)._container.parentNode
    ) {
      initializeMap.call(this);
      return;
    }

    if (this.marker) {
      const currentPos = this.marker.getLatLng();
      if (
        Math.abs(currentPos.lat - lat) > POSITION_EPSILON ||
        Math.abs(currentPos.lng - lon) > POSITION_EPSILON
      ) {
        this.marker.setLatLng([lat, lon]);

        this.marker.setPopupContent(
          `<strong>${callsign}</strong><br/>Lat: ${lat.toFixed(6)}<br/>Lon: ${lon.toFixed(6)}`,
        );

        this.map!.panTo([lat, lon], { animate: true, duration: 1 });
      }

      if (symbolHtml !== this.lastSymbolHtml) {
        const markerIcon = createMarkerIcon(symbolHtml);
        this.marker.setIcon(markerIcon);
        this.lastSymbolHtml = symbolHtml;
      }
    }
  },

  destroyed(this: InfoMapContext) {
    this.destroyed = true;
    if (this.resizeTimer) {
      clearTimeout(this.resizeTimer);
      this.resizeTimer = null;
    }
    if (this.map) {
      this.map.remove();
      this.map = null;
    }
    this.marker = null;
    this.lastSymbolHtml = null;
    this.initializing = false;
  },
};

function initializeMap(this: InfoMapContext) {
  if (this.initializing || this.destroyed) return;

  if (typeof L === "undefined") {
    console.error("Leaflet not loaded for InfoMap");
    return;
  }

  this.initializing = true;

  const lat = parseFloat(this.el.dataset.lat || "");
  const lon = parseFloat(this.el.dataset.lon || "");
  const zoom = parseInt(this.el.dataset.zoom || "13") || 13;
  const callsign = this.el.dataset.callsign || "unknown";
  const symbolHtml = this.el.dataset.symbolHtml || null;

  if (!isValidCoordinate(lat, lon)) {
    console.warn(
      `InfoMap: Invalid coordinates lat=${lat}, lon=${lon} for ${callsign}`,
    );
    this.initializing = false;
    return;
  }

  const loadingEl = this.el.querySelector(
    `#${this.el.id}-loading`,
  ) as HTMLElement | null;
  if (loadingEl) {
    loadingEl.style.display = "none";
  }

  try {
    this.map = L.map(this.el, {
      center: [lat, lon],
      zoom: zoom,
      scrollWheelZoom: false,
      zoomControl: true,
      attributionControl: true,
    });

    L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
      attribution:
        '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
      maxZoom: 19,
    }).addTo(this.map);

    const markerIcon = createMarkerIcon(symbolHtml);
    this.lastSymbolHtml = symbolHtml;

    this.marker = L.marker([lat, lon], { icon: markerIcon })
      .addTo(this.map)
      .bindPopup(
        `<strong>${callsign}</strong><br/>Lat: ${lat.toFixed(6)}<br/>Lon: ${lon.toFixed(6)}`,
      );

    const map = this.map;
    this.resizeTimer = setTimeout(() => {
      if (!this.destroyed && this.el.isConnected && map && this.map === map) {
        map.invalidateSize();
      }
      this.resizeTimer = null;
    }, 250);

    this.initializing = false;
  } catch (error) {
    console.error("Error initializing InfoMap:", error);
    this.initializing = false;
  }
}

function createMarkerIcon(symbolHtml: string | null): import("leaflet").DivIcon {
  if (symbolHtml) {
    return L.divIcon({
      html: symbolHtml,
      className: "aprs-info-marker",
      iconSize: [32, 32],
      iconAnchor: [16, 16],
    });
  } else {
    return L.divIcon({
      html: `<div style="
        width: 24px;
        height: 24px;
        background-color: #3b82f6;
        border: 3px solid white;
        border-radius: 50%;
        box-shadow: 0 2px 5px rgba(0,0,0,0.3);
      "></div>`,
      className: "",
      iconSize: [24, 24],
      iconAnchor: [12, 12],
    });
  }
}
