"use client";

import {
  useState,
  type FocusEvent,
  type KeyboardEvent,
  type PointerEvent,
} from "react";
import { countryGeometries, type CountryGeometry } from "./country-geometries";
import type { UserNationalityItem } from "./types";

const countryAliases: Record<string, string> = {
  "republic of indonesia": "IDN",
  "united states": "USA",
  "united states of america": "USA",
  usa: "USA",
  us: "USA",
  uk: "GBR",
  "great britain": "GBR",
  england: "GBR",
  "russian federation": "RUS",
  russia: "RUS",
  "viet nam": "VNM",
  "south korea": "KOR",
  "republic of korea": "KOR",
  "north korea": "PRK",
  "democratic people's republic of korea": "PRK",
  "iran islamic republic of": "IRN",
  "ivory coast": "CIV",
  "cote d ivoire": "CIV",
  "czech republic": "CZE",
  "democratic republic of the congo": "COD",
  "dr congo": "COD",
  "republic of the congo": "COG",
  "east timor": "TLS",
  "brunei darussalam": "BRN",
  "lao people's democratic republic": "LAO",
  "moldova republic of": "MDA",
  "syrian arab republic": "SYR",
  "tanzania united republic of": "TZA",
  "bolivia plurinational state of": "BOL",
  "venezuela bolivarian republic of": "VEN",
  "palestine state of": "PSE",
  "taiwan province of china": "TWN",
  "turkiye": "TUR",
  turkey: "TUR",
};

type TooltipState = {
  country: CountryGeometry;
  count: number;
  left: number;
  top: number;
};

function normalizeCountry(value: string) {
  return value
    .normalize("NFKD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, " ")
    .trim();
}

function getCountryCounts(countries: UserNationalityItem[]) {
  const identifiers = new Map<string, string>();

  for (const country of countryGeometries) {
    for (const identifier of [country.code2, country.code3, country.name]) {
      if (identifier) identifiers.set(normalizeCountry(identifier), country.code3);
    }
  }

  for (const [alias, code3] of Object.entries(countryAliases)) {
    identifiers.set(normalizeCountry(alias), code3);
  }

  const counts = new Map<string, number>();
  for (const country of countries) {
    const code3 = identifiers.get(normalizeCountry(country.country));
    if (!code3 || country.count <= 0) continue;
    counts.set(code3, (counts.get(code3) ?? 0) + country.count);
  }

  return counts;
}

function getTooltipPosition(
  event: Pick<PointerEvent<SVGPathElement>, "clientX" | "clientY"> | null,
  target: SVGPathElement,
  container: HTMLDivElement,
) {
  const containerBounds = container.getBoundingClientRect();
  const targetBounds = target.getBoundingClientRect();
  const clientX = event?.clientX ?? targetBounds.left + targetBounds.width / 2;
  const clientY = event?.clientY ?? targetBounds.top + targetBounds.height / 2;
  const width = 184;
  const height = 58;
  const pointX = clientX - containerBounds.left;
  const pointY = clientY - containerBounds.top;

  return {
    left: Math.max(
      4,
      Math.min(
        pointX > containerBounds.width * 0.58
          ? pointX - width - 12
          : pointX + 12,
        containerBounds.width - width - 4,
      ),
    ),
    top: Math.max(
      4,
      Math.min(
        pointY > containerBounds.height * 0.62
          ? pointY - height - 12
          : pointY + 12,
        containerBounds.height - height - 4,
      ),
    ),
  };
}

export function DemographicMap({
  countries,
}: {
  countries: UserNationalityItem[];
}) {
  const [zoom, setZoom] = useState(1);
  const [activeCode, setActiveCode] = useState<string | null>(null);
  const [selectedCode, setSelectedCode] = useState<string | null>(null);
  const [tooltip, setTooltip] = useState<TooltipState | null>(null);
  const counts = getCountryCounts(countries);

  function updateTooltip(
    country: CountryGeometry,
    event: Pick<PointerEvent<SVGPathElement>, "clientX" | "clientY"> | null,
    target: SVGPathElement,
  ) {
    const container = target.closest<HTMLDivElement>("[data-demographic-map]");
    if (!container) return;

    setActiveCode(country.code3);
    setTooltip({
      country,
      count: counts.get(country.code3) ?? 0,
      ...getTooltipPosition(event, target, container),
    });
  }

  function toggleSelection(country: CountryGeometry, target: SVGPathElement) {
    if (selectedCode === country.code3) {
      setSelectedCode(null);
      setActiveCode(null);
      setTooltip(null);
      return;
    }

    setSelectedCode(country.code3);
    updateTooltip(country, null, target);
  }

  function handlePointerMove(
    country: CountryGeometry,
    event: PointerEvent<SVGPathElement>,
  ) {
    if (event.pointerType !== "mouse" || selectedCode) return;
    updateTooltip(country, event, event.currentTarget);
  }

  function handlePointerLeave() {
    if (selectedCode) return;
    setActiveCode(null);
    setTooltip(null);
  }

  function handleKeyDown(
    country: CountryGeometry,
    event: KeyboardEvent<SVGPathElement>,
  ) {
    if (event.key !== "Enter" && event.key !== " ") return;
    event.preventDefault();
    toggleSelection(country, event.currentTarget);
  }

  function handleBlur(event: FocusEvent<SVGPathElement>) {
    if (selectedCode) return;
    const nextTarget = event.relatedTarget;
    if (nextTarget instanceof SVGPathElement) return;
    setActiveCode(null);
    setTooltip(null);
  }

  return (
    <div
      data-demographic-map
      className="relative mb-5 h-[212px] overflow-hidden rounded-lg bg-muted/20"
      onKeyDown={(event) => {
        if (event.key === "Escape" && selectedCode) {
          setSelectedCode(null);
          setActiveCode(null);
          setTooltip(null);
        }
      }}
    >
      <svg
        viewBox="0 0 600 250"
        role="group"
        aria-label="Interactive world map. Hover or focus a country to see customer count; tap to pin it."
        preserveAspectRatio="none"
        className="h-full w-full"
      >
        <g transform={`translate(${300 - 300 * zoom} ${125 - 125 * zoom}) scale(${zoom})`}>
          {countryGeometries.map((country) => {
            const count = counts.get(country.code3) ?? 0;
            const isActive = activeCode === country.code3;
            return (
              <path
                key={country.code3}
                d={country.path}
                fill={isActive ? "var(--primary)" : "#D1D1D6"}
                stroke="var(--background)"
                strokeWidth={isActive ? 0.85 : 0.45}
                className="cursor-pointer outline-none transition-colors focus-visible:stroke-primary"
                role={count > 0 ? "button" : undefined}
                tabIndex={count > 0 ? 0 : undefined}
                aria-label={
                  count > 0
                    ? `${country.name}: ${count.toLocaleString("id-ID")} customers`
                    : undefined
                }
                onPointerEnter={(event) => handlePointerMove(country, event)}
                onPointerLeave={handlePointerLeave}
                onFocus={(event) => updateTooltip(country, null, event.currentTarget)}
                onBlur={handleBlur}
                onClick={(event) => toggleSelection(country, event.currentTarget)}
                onKeyDown={(event) => handleKeyDown(country, event)}
              />
            );
          })}
        </g>
      </svg>

      {tooltip && (
        <div
          role="tooltip"
          className="pointer-events-none absolute z-10 min-w-40 rounded-md border border-border bg-popover px-3 py-2 text-xs shadow-lg"
          style={{ left: tooltip.left, top: tooltip.top }}
        >
          <p className="font-semibold">{tooltip.country.name}</p>
          <p className="text-muted-foreground">
            {tooltip.count.toLocaleString("id-ID")} customers
          </p>
        </div>
      )}

      <div className="absolute left-3 top-3 z-20 flex flex-col gap-1">
        <button
          aria-label="Zoom in on demographics"
          className="size-5 rounded bg-muted text-xs"
          onClick={() => setZoom(Math.min(3, zoom + 0.5))}
        >
          +
        </button>
        <button
          aria-label="Zoom out on demographics"
          className="size-5 rounded bg-muted text-xs"
          onClick={() => setZoom(Math.max(1, zoom - 0.5))}
        >
          −
        </button>
      </div>
    </div>
  );
}
