import { useEffect, useRef, useState, type CSSProperties } from 'react';
import { buildWorldTerrainContext, proceduralTerrain, townsApiToInfluences } from '../game/procedural';
import type { Town } from '../game/types';
import { useT } from '../i18n';

// ---- Types ----

interface MinimapProps {
  heroX: number;
  heroY: number;
  towns: Town[];
  routeWaypoints: Array<{ x: number; y: number }> | null;
}

/** 0 = свернуто, 1 = маленькая, 2 = большая */
type MapMode = 0 | 1 | 2;

// ---- Constants ----

const SIZE_SMALL = 120;
const SIZE_LARGE = 240;

/** Each pixel represents this many world units */
const WORLD_UNITS_PER_PX = 10;
/** Only redraw when hero has moved at least this many world units */
const REDRAW_THRESHOLD = 5;

const TERRAIN_BG: Record<string, string> = {
  grass: '#3a7a28',
  road: '#8e7550',
  dirt: '#7c6242',
  stone: '#6c7078',
  plaza: '#6c6c75',
  forest_floor: '#2d5a24',
  ruins_floor: '#5a5a58',
  canyon_floor: '#8b7355',
  swamp_floor: '#3d5c42',
  volcanic_floor: '#5c3830',
  astral_floor: '#4a4580',
};

const DEFAULT_BG = '#1e2420';

function hexToRgb(hex: string): [number, number, number] {
  const s = hex.replace('#', '');
  return [
    parseInt(s.slice(0, 2), 16),
    parseInt(s.slice(2, 4), 16),
    parseInt(s.slice(4, 6), 16),
  ];
}

const TERRAIN_RGB = new Map<string, [number, number, number]>(
  Object.entries(TERRAIN_BG).map(([k, v]) => [k, hexToRgb(v)]),
);
const DEFAULT_RGB = hexToRgb(DEFAULT_BG);

// ---- Styles ----

const containerStyle: CSSProperties = {
  position: 'fixed',
  top: 12,
  right: 8,
  zIndex: 40,
  userSelect: 'none',
};

const toggleBtnStyle: CSSProperties = {
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  gap: 4,
  padding: '3px 8px',
  fontSize: 8,
  fontWeight: 700,
  color: 'rgba(255,255,255,0.55)',
  background: 'rgba(0,0,0,0.5)',
  border: '1px solid rgba(255,255,255,0.15)',
  borderRadius: 6,
  cursor: 'pointer',
  fontFamily: 'monospace',
  letterSpacing: 1,
  WebkitTapHighlightColor: 'transparent',
  marginBottom: 2,
  marginLeft: 'auto',
  pointerEvents: 'auto',
};

function canvasStyleForSize(px: number): CSSProperties {
  return {
    width: px,
    height: px,
    borderRadius: 8,
    border: '2px solid rgba(255,255,255,0.2)',
    boxShadow: '0 2px 12px rgba(0,0,0,0.6)',
    display: 'block',
  };
}

function modeLabel(mode: MapMode, mapStr: string): string {
  if (mode === 0) return `${mapStr} \u25B6`;
  if (mode === 1) return `${mapStr} S`;
  return `${mapStr} L`;
}

// ---- Component ----

export function Minimap({ heroX, heroY, towns, routeWaypoints }: MinimapProps) {
  const tr = useT();
  const [mode, setMode] = useState<MapMode>(1);
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const lastDrawPos = useRef<{ x: number; y: number }>({ x: NaN, y: NaN });
  const lastHeroTile = useRef<{ tx: number; ty: number } | null>(null);
  const lastRouteKey = useRef<string>('');
  const lastTownsKey = useRef<string>('');

  const size = mode === 2 ? SIZE_LARGE : SIZE_SMALL;
  const collapsed = mode === 0;

  useEffect(() => {
    if (!collapsed) {
      lastDrawPos.current = { x: NaN, y: NaN };
      lastHeroTile.current = null;
      lastRouteKey.current = '';
      lastTownsKey.current = '';
    }
  }, [collapsed, size]);

  useEffect(() => {
    if (collapsed) return;

    const routeKey =
      routeWaypoints && routeWaypoints.length >= 2
        ? routeWaypoints.map((p) => `${p.x},${p.y}`).join(';')
        : '';
    const routeChanged = routeKey !== lastRouteKey.current;

    const townsKey =
      towns.length === 0
        ? ''
        : towns.map((t) => `${t.id}:${t.worldX}:${t.worldY}`).join(';');
    const townsChanged = townsKey !== lastTownsKey.current;

    const tileX = Math.floor(heroX);
    const tileY = Math.floor(heroY);
    const last = lastDrawPos.current;
    const dx = Math.abs(heroX - last.x);
    const dy = Math.abs(heroY - last.y);
    const lt = lastHeroTile.current;
    const tileChanged = !lt || lt.tx !== tileX || lt.ty !== tileY;
    if (
      !routeChanged &&
      !townsChanged &&
      !tileChanged &&
      dx < REDRAW_THRESHOLD &&
      dy < REDRAW_THRESHOLD
    ) {
      return;
    }
    lastRouteKey.current = routeKey;
    lastTownsKey.current = townsKey;

    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    lastDrawPos.current = { x: heroX, y: heroY };
    lastHeroTile.current = { tx: tileX, ty: tileY };

    const w = canvas.width;
    const h = canvas.height;
    const cx = w / 2;
    const cy = h / 2;
    const minDim = Math.min(w, h);
    const gridStep = Math.max(10, Math.round(minDim / 6));
    const fontLetter = `${Math.max(7, Math.round(minDim * 0.067))}px monospace`;
    const fontTown = `${Math.max(5, Math.round(minDim * 0.048))}px monospace`;
    const margin = Math.max(4, Math.round(minDim * 0.05));
    const heroR = Math.max(3, Math.round(minDim * 0.035));
    const glowR = Math.max(8, Math.round(minDim * 0.09));

    const activeRoute =
      routeWaypoints && routeWaypoints.length >= 2 ? routeWaypoints : null;
    const miniCtx =
      towns.length === 0
        ? null
        : buildWorldTerrainContext(townsApiToInfluences(towns), activeRoute);

    if (!miniCtx || miniCtx.towns.length === 0) {
      const terrain = proceduralTerrain(tileX, tileY, null);
      ctx.fillStyle = TERRAIN_BG[terrain] ?? DEFAULT_BG;
      ctx.fillRect(0, 0, w, h);
    } else {
      const img = ctx.createImageData(w, h);
      const data = img.data;
      let q = 0;
      const scale = WORLD_UNITS_PER_PX;
      for (let py = 0; py < h; py++) {
        const wy = Math.floor(heroY + (py - cy) * scale);
        for (let px = 0; px < w; px++) {
          const wx = Math.floor(heroX + (px - cx) * scale);
          const terrain = proceduralTerrain(wx, wy, miniCtx);
          const rgb = TERRAIN_RGB.get(terrain) ?? DEFAULT_RGB;
          data[q++] = rgb[0];
          data[q++] = rgb[1];
          data[q++] = rgb[2];
          data[q++] = 255;
        }
      }
      ctx.putImageData(img, 0, 0);
    }

    ctx.strokeStyle = 'rgba(255, 255, 255, 0.06)';
    ctx.lineWidth = 0.5;
    for (let gx = 0; gx < w; gx += gridStep) {
      ctx.beginPath(); ctx.moveTo(gx, 0); ctx.lineTo(gx, h); ctx.stroke();
    }
    for (let gy = 0; gy < h; gy += gridStep) {
      ctx.beginPath(); ctx.moveTo(0, gy); ctx.lineTo(w, gy); ctx.stroke();
    }

    for (const town of towns) {
      const relX = (town.worldX - heroX) / WORLD_UNITS_PER_PX;
      const relY = (town.worldY - heroY) / WORLD_UNITS_PER_PX;
      let px = cx + relX;
      let py = cy + relY;

      const clamped =
        px < margin || px > w - margin || py < margin || py > h - margin;
      px = Math.max(margin, Math.min(w - margin, px));
      py = Math.max(margin, Math.min(h - margin, py));

      const radius = clamped ? heroR * 0.95 : heroR + 1;
      ctx.beginPath();
      ctx.arc(px, py, radius, 0, Math.PI * 2);
      ctx.fillStyle = '#daa520';
      ctx.fill();
      ctx.strokeStyle = '#ffd700';
      ctx.lineWidth = 1.2;
      ctx.stroke();

      const letter = town.name.charAt(0).toUpperCase();
      ctx.fillStyle = '#000';
      ctx.font = `bold ${fontLetter}`;
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText(letter, px, py + 0.5);

      if (!clamped) {
        ctx.fillStyle = 'rgba(255,255,255,0.7)';
        ctx.font = fontTown;
        ctx.fillText(town.name, px, py + radius + Math.max(5, minDim * 0.04));
      }
    }

    const grad = ctx.createRadialGradient(cx, cy, 0, cx, cy, glowR);
    grad.addColorStop(0, 'rgba(0, 255, 255, 0.3)');
    grad.addColorStop(1, 'rgba(0, 255, 255, 0)');
    ctx.fillStyle = grad;
    ctx.fillRect(cx - glowR, cy - glowR, glowR * 2, glowR * 2);

    ctx.beginPath();
    ctx.arc(cx, cy, heroR, 0, Math.PI * 2);
    ctx.fillStyle = '#00ffff';
    ctx.fill();
    ctx.strokeStyle = '#ffffff';
    ctx.lineWidth = 1.5;
    ctx.stroke();
  }, [heroX, heroY, towns, routeWaypoints, collapsed, size]);

  return (
    <div style={containerStyle}>
      <button
        type="button"
        style={toggleBtnStyle}
        title="Карта: свернуть / малый / большой"
        onClick={() => setMode((m) => ((((m + 1) % 3) as MapMode)))}
      >
        {modeLabel(mode, tr.map)}
      </button>
      {!collapsed && (
        <canvas
          ref={canvasRef}
          width={size}
          height={size}
          style={canvasStyleForSize(size)}
        />
      )}
    </div>
  );
}
