<script>
  import { onMount } from 'svelte';
  import * as d3 from 'd3';
  import { feature } from 'topojson-client';
  import worldData from 'world-atlas/countries-50m.json';
  import { getSelected, toggle, setTotalCount } from '../layout/selection.svelte.js';

  const TERRITORY_PARENT = {
    '016': '840', '060': '826', '086': '826', '092': '826', '136': '826',
    '184': '554', '234': '208', '238': '826', '239': '826', '248': '246',
    '258': '250', '260': '250', '304': '208', '316': '840', '334': '036',
    '446': '156', '500': '826', '531': '528', '533': '528', '534': '528',
    '540': '250', '570': '554', '574': '036', '580': '840', '612': '826',
    '630': '840', '652': '250', '654': '826', '660': '826', '663': '250',
    '666': '250', '796': '826', '831': '826', '832': '826', '833': '826',
    '850': '840', '876': '250',
  };

  function effId(d) {
    return TERRITORY_PARENT[d.id] || d.id;
  }

  let frameEl;

  function fitProjection(proj, w, h) {
    proj.fitSize([w, h], { type: 'Sphere' });
    const s = proj.scale() * 1.5;
    proj.scale(s).translate([w / 2, h * 0.70]);
  }

  onMount(() => {
    const width = frameEl.clientWidth;
    const height = frameEl.clientHeight;

    const projection = d3.geoMercator();
    fitProjection(projection, width, height);

    const path = d3.geoPath().projection(projection);

    const countries = feature(worldData, worldData.objects.countries)
      .features.filter(f => (f.id || f.properties.name === 'Kosovo') && f.id !== '010');

    countries.forEach(f => { if (!f.id) f.id = 'XK'; });

    const sovereignIds = new Set(countries.map(f => effId(f)));
    setTotalCount(sovereignIds.size);

    const svg = d3.select(frameEl).append('svg').attr('width', width).attr('height', height);
    const g = svg.append('g');

    const tooltip = d3.select(frameEl).append('div').attr('class', 'tooltip').style('display', 'none');

    function updateFill(sel) {
      sel.attr('fill', d => getSelected().has(effId(d)) ? '#22c55e' : '#ffffff');
      g.selectAll('.micro-state').attr('fill', d => getSelected().has(effId(d)) ? '#22c55e' : '#ffffff');
    }

    function attachEvents(sel) {
      sel
        .on('click', (event, d) => {
          toggle(effId(d));
          updateFill(d3.select(event.currentTarget));
        })
        .on('mouseenter', (event, d) => {
          d3.select(event.currentTarget).attr('fill', getSelected().has(effId(d)) ? '#16a34a' : '#f0f6fa');
          tooltip.style('display', 'block').text(d.properties.name);
        })
        .on('mousemove', (event) => {
          const [x, y] = d3.pointer(event, frameEl);
          tooltip.style('left', (x + 10) + 'px').style('top', (y - 28) + 'px');
        })
        .on('mouseleave', (event, d) => {
          d3.select(event.currentTarget).attr('fill', getSelected().has(effId(d)) ? '#22c55e' : '#ffffff');
          tooltip.style('display', 'none');
        });
    }

    const paths = g.selectAll('path').data(countries).join('path')
      .attr('d', path).attr('fill', '#ffffff').attr('stroke', '#d4d4d4').attr('stroke-width', 0.5);
    attachEvents(paths);

    function renderMicrostates() {
      g.selectAll('.micro-state').remove();
      const threshold = Math.max(4, 16 / d3.zoomTransform(svg.node()).k);
      paths.each(function (d) {
        if (effId(d) !== d.id) return;
        const { width, height } = this.getBBox();
        if (width < threshold && height < threshold) {
          const [cx, cy] = path.centroid(d);
          const c = g.append('circle').attr('class', 'micro-state').datum(d)
            .attr('cx', cx).attr('cy', cy).attr('r', 2)
            .attr('fill', getSelected().has(effId(d)) ? '#22c55e' : '#ffffff')
            .attr('stroke', '#94a3b8').attr('stroke-width', 0.5);
          attachEvents(c);
        }
      });
    }

    renderMicrostates();

    const zoom = d3.zoom().scaleExtent([1, 32]).on('zoom', (event) => {
      g.attr('transform', event.transform);
      renderMicrostates();
    });

    svg.call(zoom);
    svg.on('dblclick.zoom', null);
    svg.on('dblclick', (event) => {
      const [x, y] = d3.pointer(event);
      svg.transition().duration(300).call(zoom.scaleBy, 2, [x, y]);
    });

    const observer = new ResizeObserver((entries) => {
      for (const entry of entries) {
        const { width, height } = entry.contentRect;
        svg.attr('width', width).attr('height', height);
        fitProjection(projection, width, height);
        const countryPaths = g.selectAll('path');
        countryPaths.attr('d', path);
        updateFill(countryPaths);
        renderMicrostates();
      }
    });

    observer.observe(frameEl);

    return () => {
      observer.disconnect();
      svg.remove();
    };
  });
</script>

<div bind:this={frameEl} class="map-frame"></div>

<style>
  .map-frame {
    width: 100%;
    height: 100%;
    overflow: hidden;
    position: relative;
    background: #a4c8e0;
  }

  .map-frame :global(svg) { display: block; cursor: grab; }
  .map-frame :global(svg:active) { cursor: grabbing; }
  .map-frame :global(svg path) { cursor: pointer; }

  .map-frame :global(.tooltip) {
    position: absolute;
    padding: 4px 10px;
    background: #1f2937;
    color: #fff;
    font: 14px/1.4 sans-serif;
    border-radius: 4px;
    pointer-events: none;
    white-space: nowrap;
  }
</style>
