<script lang="ts">
import { onMount, onDestroy, createEventDispatcher } from 'svelte';
import { BACKEND_URL } from '../../constants/config';
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';

const TILE_ZOOM = 11;
const TILE_PIXELS = 1024;
const SEASON = 's0';

type PixelEventDetail = { tileX: number; tileY: number; x: number; y: number; latlng: { lat: number; lng: number } };

const dispatch = createEventDispatcher<{ pixel: PixelEventDetail }>();

let mapContainer: HTMLDivElement;
let map: maplibregl.Map | null = null;
let marker: maplibregl.Marker | null = null;

export let center: [number, number] = [0, 0];

onMount(async () => {
  // Load the liberty style from static files
  const styleResponse = await fetch('/maps/styles/liberty');
  const baseStyle = await styleResponse.json();

  // Add pixel tiles source and layer to the style
  baseStyle.sources['pixel-tiles'] = {
    type: 'raster',
    tiles: [`${BACKEND_URL}/files/${SEASON}/tiles/{x}/{y}.png`],
    tileSize: 256,
    minzoom: TILE_ZOOM,
    maxzoom: TILE_ZOOM
  };

  baseStyle.layers.push({
    id: 'pixel-layer',
    type: 'raster',
    source: 'pixel-tiles',
    minzoom: TILE_ZOOM,
    maxzoom: TILE_ZOOM,
    paint: {
      'raster-opacity': 0.8
    }
  });

  map = new maplibregl.Map({
    container: mapContainer,
    style: baseStyle,
    center: center as [number, number],
    zoom: 2,
    minZoom: 0,
    maxZoom: 22
  });

  map.on('click', (event) => {
    if (!map) return;
    const { lng, lat } = event.lngLat;
    const info = latLngToTile(lat, lng);
    if (!info) return;

    if (!marker) {
      const el = document.createElement('div');
      el.className = 'w-3 h-3 bg-red-500 border-2 border-white rounded-full';
      marker = new maplibregl.Marker({ element: el })
        .setLngLat([lng, lat])
        .addTo(map!);
    } else {
      marker.setLngLat([lng, lat]);
    }

    dispatch('pixel', { ...info, latlng: { lat, lng } });
  });
});

onDestroy(() => {
  marker?.remove();
  map?.remove();
  map = null;
  marker = null;
});

export function focusPixel(tileX: number, tileY: number, x: number, y: number) {
  if (!map) return;
  const { lat, lng } = tileToLatLng(tileX, tileY, x, y);
  map.flyTo({ center: [lng, lat], essential: true });

  if (!marker) {
    const el = document.createElement('div');
    el.className = 'w-3 h-3 bg-red-500 border-2 border-white rounded-full';
    marker = new maplibregl.Marker({ element: el })
      .setLngLat([lng, lat])
      .addTo(map);
  } else {
    marker.setLngLat([lng, lat]);
  }
}

export function refreshTiles() {
  if (!map) return;
  const source = map.getSource('pixel-tiles') as maplibregl.RasterTileSource;
  if (source) {
    // Force tile refresh by clearing cache
    map.style.sourceCaches['pixel-tiles']?.clearTiles();
    map.triggerRepaint();
  }
}

export function getMap() {
  return map;
}

function latLngToTile(lat: number, lng: number) {
  const n = Math.pow(2, TILE_ZOOM);
  const xFloat = ((lng + 180) / 360) * n;
  const latRad = (lat * Math.PI) / 180;
  const yFloat = (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2 * n;
  const tileX = Math.floor(xFloat);
  const tileY = Math.floor(yFloat);
  if (tileX < 0 || tileX >= n || tileY < 0 || tileY >= n) {
    return null;
  }
  const localX = Math.floor((xFloat - tileX) * TILE_PIXELS);
  const localY = Math.floor((yFloat - tileY) * TILE_PIXELS);
  return { tileX, tileY, x: localX, y: localY };
}

function tileToLatLng(tileX: number, tileY: number, x: number, y: number) {
  const n = Math.pow(2, TILE_ZOOM);
  const fx = tileX + x / TILE_PIXELS;
  const fy = tileY + y / TILE_PIXELS;
  const lng = (fx / n) * 360 - 180;
  const latRad = Math.atan(Math.sinh(Math.PI * (1 - (2 * fy) / n)));
  const lat = (latRad * 180) / Math.PI;
  return { lat, lng };
}
</script>

<div bind:this={mapContainer} class="w-full h-full rounded-box overflow-hidden shadow-lg border border-base-300"></div>
