// ============================================================
// India Map — d3-geo topojson rendering + festival icon pins
// Includes Tricolor mode for Republic Day (W4) & Independence Day (W32)
// ============================================================
/* global React, d3, topojson, LOF_Icons */

const { useEffect, useMemo, useRef, useState } = React;
const { FestivalIcon, pickIcon } = LOF_Icons;

const STATE_ZONE = {
  'Jammu and Kashmir':'North','Ladakh':'North','Himachal Pradesh':'North','Punjab':'North',
  'Haryana':'North','Uttarakhand':'North','Delhi':'North','Chandigarh':'North',
  'Rajasthan':'West','Gujarat':'West','Maharashtra':'West','Goa':'West',
  'Dadra and Nagar Haveli and Daman and Diu':'West',
  'Madhya Pradesh':'Central','Chhattisgarh':'Central','Uttar Pradesh':'Central',
  'Bihar':'East','Jharkhand':'East','West Bengal':'East','Odisha':'East',
  'Assam':'Northeast','Arunachal Pradesh':'Northeast','Nagaland':'Northeast',
  'Manipur':'Northeast','Mizoram':'Northeast','Tripura':'Northeast',
  'Meghalaya':'Northeast','Sikkim':'Northeast',
  'Karnataka':'South','Kerala':'South','Tamil Nadu':'South','Andhra Pradesh':'South',
  'Telangana':'South','Puducherry':'South',
  'Lakshadweep':'Islands','Andaman and Nicobar Islands':'Islands',
};

const ZONE_FILL = {
  North:'oklch(0.32 0.028 70)', West:'oklch(0.32 0.032 50)',
  Central:'oklch(0.32 0.026 40)', East:'oklch(0.32 0.026 25)',
  Northeast:'oklch(0.32 0.028 160)', South:'oklch(0.32 0.032 80)',
  Islands:'oklch(0.32 0.024 220)',
};
const ZONE_FILL_BRIGHT = {
  North:'oklch(0.46 0.046 70)', West:'oklch(0.46 0.052 50)',
  Central:'oklch(0.46 0.044 40)', East:'oklch(0.46 0.044 25)',
  Northeast:'oklch(0.46 0.046 160)', South:'oklch(0.46 0.052 80)',
  Islands:'oklch(0.46 0.040 220)',
};

const TRICOLOR_WEEKS = new Set([4, 32]);

function IndiaMap({
  topo, width, height, pins, week,
  hoveredPin, setHoveredPin,
  selectedPin, setSelectedPin,
}) {
  const tricolor = TRICOLOR_WEEKS.has(week);

  const { features, path, stateCentroids, panCenter, bounds } = useMemo(() => {
    if (!topo) return {};
    const fc = topojson.feature(topo, topo.objects.states);
    const proj = d3.geoMercator();
    proj.fitExtent([[8, 8], [width - 8, height - 8]], fc);
    const pth = d3.geoPath(proj);

    const centroids = {};
    fc.features.forEach((f) => {
      const c = d3.geoCentroid(f);
      const xy = proj(c);
      if (xy) centroids[f.properties.st_nm] = { x: xy[0], y: xy[1] };
    });
    const panCtr = proj([78.6, 22.5]);

    const b = pth.bounds(fc);
    return {
      features: fc.features, path: pth, stateCentroids: centroids,
      panCenter: { x: panCtr[0], y: panCtr[1] },
      bounds: b,
    };
  }, [topo, width, height]);

  const resolvedPins = useMemo(() => {
    if (!stateCentroids) return [];
    return pins
      .map((p) => {
        if (p.pan) return { ...p, x: panCenter.x, y: panCenter.y, isPan: true };
        const c = stateCentroids[p.state];
        if (!c) return null;
        return { ...p, x: c.x, y: c.y };
      })
      .filter(Boolean);
  }, [pins, stateCentroids, panCenter]);

  const pinNodes = useMemo(() => {
    const groups = new Map();
    resolvedPins.forEach((p) => {
      const key = p.isPan ? '__pan__' : p.state;
      if (!groups.has(key)) groups.set(key, []);
      groups.get(key).push(p);
    });
    const out = [];
    groups.forEach((items) => {
      const cx = items[0].x, cy = items[0].y;
      items.forEach((p, i) => {
        const n = items.length;
        const r = n === 1 ? 0 : 11;
        const a = (i / n) * Math.PI * 2 - Math.PI / 2;
        out.push({
          ...p,
          x: cx + Math.cos(a) * r,
          y: cy + Math.sin(a) * r,
          clusterSize: n,
        });
      });
    });
    return out;
  }, [resolvedPins]);

  const activeStates = useMemo(() => {
    const s = new Set();
    resolvedPins.forEach((p) => { if (!p.isPan && p.state) s.add(p.state); });
    return s;
  }, [resolvedPins]);

  if (!topo) return null;

  const hoveredNode = pinNodes.find((n) => n.id === hoveredPin || n.id === selectedPin);

  return (
    <div style={{
      position: 'relative',
      width, height,
      maxWidth: '100%', maxHeight: '100%',
    }}>
      <svg
        viewBox={`0 0 ${width} ${height}`}
        width={width} height={height}
        style={{ display: 'block', overflow: 'visible' }}
      >
        <defs>
          {bounds && (
            <linearGradient
              id="tiranga"
              gradientUnits="userSpaceOnUse"
              x1={0} y1={bounds[0][1]} x2={0} y2={bounds[1][1]}
            >
              <stop offset="0"      stopColor="#FF9933" />
              <stop offset="0.333"  stopColor="#FF9933" />
              <stop offset="0.333"  stopColor="#FFFFFF" />
              <stop offset="0.666"  stopColor="#FFFFFF" />
              <stop offset="0.666"  stopColor="#138808" />
              <stop offset="1"      stopColor="#138808" />
            </linearGradient>
          )}

          <pattern id="stipple" x="0" y="0" width="6" height="6" patternUnits="userSpaceOnUse">
            <circle cx="3" cy="3" r="0.45" fill="oklch(0.55 0.028 65)" opacity="0.35" />
          </pattern>

          <filter id="soft-glow" x="-50%" y="-50%" width="200%" height="200%">
            <feGaussianBlur stdDeviation="3" />
          </filter>
        </defs>

        <g>
          {features.map((f, i) => (
            <path
              key={'halo-' + i}
              d={path(f)}
              fill="none"
              stroke={tricolor ? 'oklch(0.85 0.18 75 / 0.35)' : 'oklch(0.78 0.155 65 / 0.10)'}
              strokeWidth={tricolor ? 18 : 14}
              strokeLinejoin="round"
              style={{ transition: 'stroke 700ms ease, stroke-width 700ms ease' }}
            />
          ))}
        </g>

        <g>
          {features.map((f, i) => {
            const name = f.properties.st_nm;
            const zone = STATE_ZONE[name] || 'Central';
            const active = activeStates.has(name);
            const fill = tricolor ? 'url(#tiranga)' : (active ? ZONE_FILL_BRIGHT[zone] : ZONE_FILL[zone]);
            return (
              <path
                key={'fill-' + i}
                d={path(f)}
                fill={fill}
                stroke={tricolor ? 'rgba(255,255,255,0.45)' : 'oklch(0.60 0.025 60 / 0.50)'}
                strokeWidth={tricolor ? 0.5 : 0.6}
                strokeLinejoin="round"
                style={{ transition: 'fill 600ms ease, stroke 600ms ease' }}
              />
            );
          })}
          {!tricolor && features.map((f, i) => (
            <path
              key={'stipple-' + i}
              d={path(f)}
              fill="url(#stipple)"
              stroke="none"
              opacity="0.55"
            />
          ))}
        </g>

        {!tricolor && (
          <g style={{ mixBlendMode: 'screen' }}>
            {features.map((f, i) => (
              <path key={'edge-'+i} d={path(f)} fill="none"
                stroke="oklch(0.92 0.04 70 / 0.18)" strokeWidth="0.4" />
            ))}
          </g>
        )}

        {tricolor && bounds && (
          <AshokChakra
            cx={(bounds[0][0] + bounds[1][0]) / 2}
            cy={bounds[0][1] + (bounds[1][1] - bounds[0][1]) * 0.5}
            r={Math.min(width, height) * 0.10}
          />
        )}

        {!tricolor && pinNodes.some((p) => p.isPan) && (
          <g transform={`translate(${panCenter.x},${panCenter.y})`} pointerEvents="none">
            {[0, 1, 2].map((i) => (
              <circle key={i} r={24 + i * 14} fill="none"
                stroke="oklch(0.84 0.135 88 / 0.32)" strokeWidth="1"
                style={{
                  animation: 'pulseRing 2.6s ease-out infinite',
                  animationDelay: `${i * 0.8}s`,
                  transformOrigin: 'center',
                }} />
            ))}
          </g>
        )}
      </svg>

      <div style={{
        position: 'absolute', inset: 0, pointerEvents: 'none',
      }}>
        {pinNodes.map((p) => (
          <PinIcon
            key={p.id + '-' + p.state}
            p={p}
            hovered={hoveredPin === p.id}
            selected={selectedPin === p.id}
            onEnter={() => setHoveredPin(p.id)}
            onLeave={() => setHoveredPin(null)}
            onClick={() => setSelectedPin(p.id)}
            dim={tricolor}
          />
        ))}

        {hoveredNode && (
          <HoverCard node={hoveredNode} width={width} height={height} />
        )}
      </div>
    </div>
  );
}

function AshokChakra({ cx, cy, r }) {
  return (
    <g
      transform={`translate(${cx},${cy})`}
      style={{
        transformOrigin: 'center',
        animation: 'chakraSpin 16s linear infinite',
        transformBox: 'fill-box',
      }}
    >
      <circle r={r * 1.4} fill="#000080" opacity="0.08" />
      <circle r={r} fill="none" stroke="#000080" strokeWidth={r * 0.10} />
      <circle r={r * 0.85} fill="none" stroke="#000080" strokeWidth={r * 0.04} />
      <circle r={r * 0.10} fill="#000080" />
      {Array.from({ length: 24 }).map((_, i) => {
        const a = (i / 24) * Math.PI * 2;
        return (
          <line
            key={i}
            x1={Math.cos(a) * r * 0.10}
            y1={Math.sin(a) * r * 0.10}
            x2={Math.cos(a) * r * 0.85}
            y2={Math.sin(a) * r * 0.85}
            stroke="#000080"
            strokeWidth={r * 0.04}
            strokeLinecap="round"
          />
        );
      })}
    </g>
  );
}

function PinIcon({ p, hovered, selected, onEnter, onLeave, onClick, dim }) {
  const rule = pickIcon(p.festival);
  const Icon = LOF_Icons.Icons[rule.icon] || LOF_Icons.Icons.Star;
  const size = (hovered || selected) ? 44 : (p.clusterSize > 1 ? 30 : 34);
  const z = (hovered || selected) ? 25 : 10;

  return (
    <div
      onMouseEnter={onEnter}
      onMouseLeave={onLeave}
      onClick={onClick}
      style={{
        position: 'absolute',
        left: p.x, top: p.y,
        width: size, height: size,
        transform: 'translate(-50%, -50%)',
        cursor: 'pointer',
        pointerEvents: 'auto',
        zIndex: z,
        opacity: dim ? 0.0 : 1,
        transition: 'width 220ms ease, height 220ms ease, opacity 600ms ease',
        animation: 'pinDrop 600ms ease both',
      }}
    >
      <div style={{
        position: 'absolute',
        inset: -6,
        borderRadius: '50%',
        background: `radial-gradient(circle, ${rule.tint}55 0%, transparent 65%)`,
        filter: 'blur(2px)',
      }} />
      <div style={{
        position: 'relative',
        width: '100%', height: '100%',
        borderRadius: '50%',
        boxShadow: (hovered || selected)
          ? `0 0 18px ${rule.tint}cc, 0 0 0 2px rgba(255,246,229,0.85)`
          : `0 0 8px ${rule.tint}80`,
        animation: (hovered || selected) ? 'iconHover 1.2s ease-in-out infinite' : 'none',
      }}>
        <Icon />
      </div>
      {p.clusterSize > 1 && (
        <div style={{
          position: 'absolute',
          top: -6, right: -6,
          background: '#1A1410',
          color: '#FFD60A',
          border: '1px solid #FFD60A',
          borderRadius: 999,
          fontSize: 9,
          fontWeight: 700,
          fontFamily: 'Inter Tight, sans-serif',
          padding: '1px 5px',
          lineHeight: 1.2,
        }}>{p.clusterSize}</div>
      )}
    </div>
  );
}

function HoverCard({ node, width, height }) {
  const cardW = 240;
  const cardH = 86;
  const margin = 16;
  const onRight = node.x + cardW + margin < width;
  const left = onRight ? node.x + 28 : node.x - 28 - cardW;
  let top = Math.max(8, Math.min(height - cardH - 8, node.y - cardH / 2));

  const rule = pickIcon(node.festival);

  return (
    <div style={{
      position: 'absolute',
      left, top,
      width: cardW,
      pointerEvents: 'none',
      animation: 'fadeUp 0.25s ease both',
      zIndex: 50,
    }}>
      <svg style={{
        position: 'absolute',
        left: onRight ? -28 : cardW,
        top: cardH / 2 - 6,
        width: 28, height: 12,
        overflow: 'visible',
      }}>
        <line
          x1={onRight ? 0 : 28} y1="6" x2={onRight ? 28 : 0} y2="6"
          stroke={rule.tint} strokeWidth="1.5" opacity="0.9"
        />
        <circle cx={onRight ? 28 : 0} cy="6" r="2" fill={rule.tint} />
      </svg>

      <div style={{
        background: 'rgba(20, 14, 8, 0.94)',
        border: `1px solid ${rule.tint}66`,
        borderLeft: `3px solid ${rule.tint}`,
        backdropFilter: 'blur(10px)',
        WebkitBackdropFilter: 'blur(10px)',
        boxShadow: `0 12px 32px rgba(0,0,0,0.55), 0 0 24px ${rule.tint}22`,
        borderRadius: 4,
        padding: '12px 14px',
        display: 'flex',
        gap: 12,
        alignItems: 'center',
      }}>
        <FestivalIcon festival={node.festival} size={42} />
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{
            fontFamily: 'Marcellus, serif',
            fontSize: 17,
            lineHeight: 1.15,
            color: '#FFF6E5',
            letterSpacing: '0.005em',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
          }}>{node.festival.name}</div>
          <div style={{
            fontFamily: 'Cormorant Garamond, serif',
            fontStyle: 'italic',
            fontSize: 13,
            color: rule.tint,
            marginTop: 2,
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
          }}>
            {node.festival.pan ? 'Across India' : (node.festival.states[0] || '')}
          </div>
          <div style={{
            fontFamily: 'Inter Tight, sans-serif',
            fontSize: 10,
            color: 'rgba(255,246,229,0.55)',
            marginTop: 4,
            textTransform: 'uppercase',
            letterSpacing: '0.12em',
          }}>
            {node.festival.timeLabel}
          </div>
        </div>
      </div>
    </div>
  );
}

(function injectMapKeyframes() {
  if (document.getElementById('lof-map-keyframes')) return;
  const s = document.createElement('style');
  s.id = 'lof-map-keyframes';
  s.textContent = `
    @keyframes chakraSpin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
    @keyframes pinDrop {
      0%   { transform: translate(-50%, -90%) scale(0.4); opacity: 0; }
      60%  { transform: translate(-50%, -45%) scale(1.1); opacity: 1; }
      100% { transform: translate(-50%, -50%) scale(1);   opacity: 1; }
    }
    @keyframes iconHover {
      0%, 100% { transform: scale(1); }
      50%      { transform: scale(1.06); }
    }
  `;
  document.head.appendChild(s);
})();

Object.assign(window, { IndiaMap, TRICOLOR_WEEKS });
