import { useRef, useState, useEffect } from "react";
import { getRadarPosition, playerColors } from "../utilities/utilities";

// ── Smooth rotation tracker ───────────────────────────────────────────────────
let playerRotations = [];
const getPlayerRotation = (playerData) => {
  const raw = playerData.m_eye_angle;
  if (raw === undefined || raw === null || isNaN(raw)) return 0;
  // CS2 yaw: 0=East, 90=North, 180=West, 270=South (pitch/yaw/roll → y = yaw),
  // CSS rotate(0°) on a downward-pointing border-triangle = South.
  // raw+90 maps CS2 yaw → CSS rotation (for the map coordinate system).
  //   0°(East) → 90°(RIGHT),  90°(North) → 180°(UP),
  //   180°(West) → 270°(LEFT), 270°(South) → 0°(DOWN).
  // Non-follow mode negates this (arrowRotation = -rotation) for CCW correction.
  // Follow-rotate other uses mapRotation - rotation (adjusted for map rotation).
  const target = raw + 90;
  const idx = playerData.m_idx;
  playerRotations[idx] = (playerRotations[idx] || 0) % 360;
  playerRotations[idx] += ((target - playerRotations[idx] + 540) % 360) - 180;
  return playerRotations[idx];
};

// Helper function to get value from object with flexible field access
// Tries primary path first, then falls back to alternative paths
const getValue = (obj, primaryPath, fallbackPaths = []) => {
  // Try primary path (dot notation)
  if (primaryPath && typeof primaryPath === 'string') {
    const value = primaryPath.split('.').reduce((obj, key) => (obj && obj[key] !== undefined) ? obj[key] : undefined, obj);
    if (value !== undefined && value !== null) return value;
  }

  // Try fallback paths
  for (const path of fallbackPaths) {
    if (typeof path === 'string') {
      const value = path.split('.').reduce((obj, key) => (obj && obj[key] !== undefined) ? obj[key] : undefined, obj);
      if (value !== undefined && value !== null) return value;
    }
  }

  return undefined;
};

// Global ammo/health/flash tracking for effects (similar to boltobserv)
// Kept for backward compatibility but effects now use signals from backend
const globalPlayerAmmo = {};
const globalPlayerHealth = {};
const globalPlayerFlash = {};

// ── Player dot ────────────────────────────────────────────────────────────────
const Player = ({ playerData, mapData, localTeam, averageLatency, settings, mapRotation = 0, imgStyle, isFollowRotate = false, lockedPlayerIdx = null }) => {
  const playerRef = useRef();
  const [lastPos, setLastPos] = useState(null);

  // Track flash start times for duration limiting (max 2.5s flashed effect)
  const flashStartTimes = useRef({});

  const radarPos = getRadarPosition(mapData, playerData.m_position) || { x: 0, y: 0 };
  const invalid = radarPos.x <= 0 && radarPos.y <= 0;
  const rotation = getPlayerRotation(playerData);
  const isSelfPlayer = settings?.whichPlayerAreYou && playerData.m_idx === Number(settings.whichPlayerAreYou);
  const isCenteredPlayer = lockedPlayerIdx !== null && playerData.m_idx === lockedPlayerIdx;
  // Arrow direction on screen depends on mode.
  const arrowRotation = (() => {
    if (playerData.m_is_dead) return 0;
    if (!isFollowRotate) return -rotation;
    // In follow-rotate mode, the centered (POV-locked) player always faces UP
    if (isSelfPlayer || isCenteredPlayer) return 180;
    return mapRotation - rotation;;
  })();

  // Avatar URL comes from backend (fetched + cached server-side).
  const avatarUrl = playerData.m_avatar_url || null;

  // Freeze last-known position when player dies
  useEffect(() => {
    if (playerData.m_is_dead) { if (!lastPos) setLastPos(radarPos); }
    else setLastPos(null);
  }, [playerData.m_is_dead]);

  const effectivePos = playerData.m_is_dead ? (lastPos || { x: 0, y: 0 }) : radarPos;

  // Use imgStyle dimensions directly — NOT getBoundingClientRect() which is
  // affected by CSS zoom/scale and returns wrong values when map is zoomed.
  // imgStyle.ml/mt are the letterbox offsets (margin from container edge to image).
  const imgW = imgStyle?.w || 1024;
  const imgH = imgStyle?.h || 1024;
  const imgMl = imgStyle?.ml ?? 0;
  const imgMt = imgStyle?.mt ?? 0;

  const dotVw = 1.15 * (settings?.dotSize ?? 1);
  const dotPx = dotVw * (imgStyle?.w || 1024) / 100;
  const dotCss = `${dotPx}px`;

  // Position relative to wrapper (which fills container).
  // Image starts at (imgMl, imgMt) inside the wrapper.
  const tx = imgMl + imgW * effectivePos.x - dotPx * 0.5;
  const ty = imgMt + imgH * effectivePos.y - dotPx * 0.5;

  const isAlly = playerData.m_team === localTeam;
  const teamColor = isAlly ? (playerColors[playerData.m_color] ?? "#84c8ed") : (settings?.enemyColor ?? "#FF4444");

  const isDead = playerData.m_is_dead;

  // Shooting effect (muzzle flash) - receive signal from backend
  const isShooting = !!getValue(playerData, 'is_shooting', [
    'shooting',
    'm_is_shooting',
    'shot',
    'm_shot',
    'isShooting'
  ]);

  // Hurt effect (health drop) - receive signal from backend
  const isHurting = !!getValue(playerData, 'is_hurting', [
    'hurting',
    'm_is_hurting',
    'damage',
    'm_damage',
    'isHurting',
    'hurt'
  ]);

  // Get flash alpha value for opacity control (similar to playercard)
  const flashAlpha = getValue(playerData, 'm_flash_alpha', [
    'flash_alpha',
    'm_flash_alpha',
    'flash',
    'm_flash'
  ]) ?? 0;

  // Flashed effect (flashbang) - show when flash alpha > 0, but limit duration to 2.5s
  const playerIdx = playerData.m_idx;
  const now = Date.now();

  // Track when flash started for this player
  if (flashAlpha > 0 && !flashStartTimes.current[playerIdx]) {
    flashStartTimes.current[playerIdx] = now;
  }

  // Clear flash start time when flash ends
  if (flashAlpha === 0) {
    delete flashStartTimes.current[playerIdx];
  }

  // Calculate if flashed (respecting 2.5s maximum duration)
  const flashStarted = flashStartTimes.current[playerIdx] || 0;
  const flashDuration = now - flashStarted;
  const isFlashedRaw = flashAlpha > 0;
  const isFlashed = isFlashedRaw && flashDuration <= 2500; // 2.5s max

  // Update global trackers for next comparison (kept for compatibility)
  // These are now optional since effects use backend signals
  const currentAmmo = getValue(playerData, 'm_weapons.m_ammo_clip', [
    'weapons.m_ammo_clip',
    'ammo',
    'm_ammo'
  ]) ?? 0;

  const currentHp = getValue(playerData, 'm_health', [
    'health',
    'm_health',
    'hp'
  ]) ?? 0;

  const currentFlash = getValue(playerData, 'm_flash_alpha', [
    'flash_alpha',
    'm_flash_alpha',
    'flash',
    'm_flash'
  ]) ?? 0;

  globalPlayerAmmo[playerData.m_idx] = currentAmmo;
  globalPlayerHealth[playerData.m_idx] = currentHp;
  globalPlayerFlash[playerData.m_idx] = currentFlash;

  return (
    <div
      ref={playerRef}
      className={`absolute left-0 top-0 player-dot ${isShooting ? 'shooting shooting-active' : ''} ${isHurting ? 'hurting hurting-active' : ''} ${isFlashed ? 'flashed flashed-flashing' : ''}`}
      data-testid="player-dot"
      style={{
        width: dotCss, height: dotCss,
        transform: `translate(${tx}px, ${ty}px) rotate(${-mapRotation}deg)`,
        // Smooth position movement: interpolate between data points.
        // Use a fixed 80ms window so dots glide regardless of latency value.
        transition: `transform 80ms linear`,
        zIndex: isDead ? 1 : 10,
        opacity: invalid ? 0 : (isDead ? 1 : (settings?.dotOpacity ?? 1)),
        filter: isDead
          ? "grayscale(100%) opacity(0.45)"
          : ((settings?.dotGlow ?? 0) > 0 ? `drop-shadow(0 0 ${(settings.dotGlow * 6).toFixed(1)}px ${teamColor})` : "none"),
        pointerEvents: "none",
        '--shooting-opacity': isShooting ? '0.3' : '0',
        '--hurting-opacity': isHurting ? '0.3' : '0',
        '--flash-opacity': Math.min(flashAlpha, 1),
        '--flash-eye-size': `${Math.min(flashAlpha * 30, 24)}px`,
      }}
    >
      {/* Weapon + name labels — stay horizontal regardless of map rotation */}
      {!isDead && (
        <div style={{
          position: "absolute",
          bottom: ((settings?.showAllNames && isAlly) || (settings?.showEnemyNames && !isAlly)) ? "185%" : "145%",
          left: "50%",
          transform: `translateX(-50%)`,
          display: "flex",
          alignItems: "center",
          gap: `${dotPx * 0.12}px`,
          pointerEvents: "none",
          opacity: settings?.weaponIconOpacity ?? 1,
        }}>
          {/* Active weapon icon (skip if it's C4 - we show C4 badge separately) */}
          {playerData.m_weapons?.m_active && playerData.m_weapons.m_active !== "c4" && (
            <div style={{
              width: `${dotPx * 0.85}px`,
              height: `${dotPx * 0.85}px`,
              WebkitMask: `url('./assets/icons/${playerData.m_weapons.m_active}.svg') no-repeat center/contain`,
              mask: `url('./assets/icons/${playerData.m_weapons.m_active}.svg') no-repeat center/contain`,
              background: teamColor,
              filter: `drop-shadow(0 1px 2px rgba(0,0,0,0.9))`,
              border: `${dotPx * 0.08}px solid ${teamColor}`,
              borderRadius: `${dotPx * 0.12}px`,
              padding: `${dotPx * 0.08}px`,
              boxSizing: "content-box",
            }} />
          )}

          {/* Grenades inventory - same size as weapon icon */}
          {playerData.m_weapons?.m_utilities?.map((util, idx) => (
            <div key={idx} style={{
              width: `${dotPx * 0.85}px`,
              height: `${dotPx * 0.85}px`,
              WebkitMask: `url('./assets/icons/${util}.svg') no-repeat center/contain`,
              mask: `url('./assets/icons/${util}.svg') no-repeat center/contain`,
              background: playerData.m_weapons.m_active === util ? teamColor : "rgba(255,255,255,0.6)",
              filter: `drop-shadow(0 1px 2px rgba(0,0,0,0.9))`,
              border: playerData.m_weapons.m_active === util ? `${dotPx * 0.08}px solid ${teamColor}` : "none",
              borderRadius: `${dotPx * 0.12}px`,
              padding: playerData.m_weapons.m_active === util ? `${dotPx * 0.08}px` : `${dotPx * 0.06}px`,
              boxSizing: "content-box",
            }} />
          ))}

          {/* C4 badge (T only) — red glow icon above player dot */}
          {playerData.m_has_bomb && (
            <div style={{
              width: `${dotPx * 0.85}px`,
              height: `${dotPx * 0.85}px`,
              WebkitMask: "url('./assets/icons/c4.svg') no-repeat center/contain",
              mask: "url('./assets/icons/c4.svg') no-repeat center/contain",
              background: "#ef4444",
              filter: "drop-shadow(0 1px 3px rgba(239,68,68,0.9)) drop-shadow(0 0 4px rgba(239,68,68,0.6))",
              flexShrink: 0,
            }} />
          )}

          {/* Defuser badge (CT only) */}
          {playerData.m_has_defuser && (
            <div style={{
              width: `${dotPx * 0.85}px`,
              height: `${dotPx * 0.85}px`,
              WebkitMask: "url('./assets/icons/defuser.svg') no-repeat center/contain",
              mask: "url('./assets/icons/defuser.svg') no-repeat center/contain",
              background: "#4a9eff",
              filter: "drop-shadow(0 1px 3px rgba(74,158,255,0.9)) drop-shadow(0 0 4px rgba(74,158,255,0.6))",
              flexShrink: 0,
            }} />
          )}
        </div>
      )}

      {/* Flag indicators — always horizontal (no map-rotation dependency) */}
      {!isDead && settings?.showIndicators !== false && (
        <div style={{
          position: "absolute",
          left: "50%",
          top: "50%",
          transform: `translate(-50%, -50%) translateX(${dotPx / 2 + dotPx * 0.6 * (settings.indicatorSize ?? 1)}px)`,
          pointerEvents: "none",
          opacity: settings?.indicatorOpacity ?? 1,
        }}>
          <div style={{
            display: "flex",
            flexDirection: "column",
            alignItems: "flex-start",
            gap: "${dotPx * 0.15 * (settings.indicatorSize ?? 1)}px",
            pointerEvents: "none",
          }}>
            {/* Ducking flag (crouch) */}
            {getValue(playerData, 'm_is_ducking', ['isDucking', 'is_ducking']) && (
              <div style={{
                fontSize: "${dotPx * 0.4 * (settings.indicatorSize ?? 1)}px",
                fontWeight: 600,
                color: settings.indicatorColors?.ducking ?? "#4a9eff", // Blue color for ducking
                textShadow: `0 0 2px ${settings.indicatorColors?.ducking ?? "#4a9eff"}`,
              }}>
                {settings.flagDisplayMode === "full" ? "Crouch" : "C"}
              </div>
            )}

            {/* Shifting flag (shift-walk) */}
            {getValue(playerData, 'm_is_shifting', ['isShifting', 'is_shifting']) && (
              <div style={{
                fontSize: "${dotPx * 0.4 * (settings.indicatorSize ?? 1)}px",
                fontWeight: 600,
                color: settings.indicatorColors?.shifting ?? "#fbbf24", // Yellow color for shifting
                textShadow: `0 0 2px ${settings.indicatorColors?.shifting ?? "#fbbf24"}`,
              }}>
                {settings.flagDisplayMode === "full" ? "Walk" : "W"}
              </div>
            )}

            {/* Scoped flag (zoomed) */}
            {getValue(playerData, 'm_is_scoped', ['isScoped']) && (
              <div style={{
                fontSize: "${dotPx * 0.4 * (settings.indicatorSize ?? 1)}px",
                fontWeight: 600,
                color: settings.indicatorColors?.scoped ?? "#10b981", // Green color for scoped
                textShadow: `0 0 2px ${settings.indicatorColors?.scoped ?? "#10b981"}`,
              }}>
                {settings.flagDisplayMode === "full" ? "Scope" : "S"}
              </div>
            )}

            {/* Reloading flag */}
            {getValue(playerData, 'm_is_reloading', ['isReloading', 'is_reloading']) && (
              <div style={{
                fontSize: "${dotPx * 0.4 * (settings.indicatorSize ?? 1)}px",
                fontWeight: 600,
                color: settings.indicatorColors?.reloading ?? "#ef4444", // Red color for reloading
                textShadow: `0 0 2px ${settings.indicatorColors?.reloading ?? "#ef4444"}`,
              }}>
                {settings.flagDisplayMode === "full" ? "Reload" : "R"}
              </div>
            )}
          </div>
        </div>
      )}

      {/* Player name label */}
      {!isDead && ((settings?.showAllNames && isAlly) || (settings?.showEnemyNames && !isAlly)) && (
        <div style={{
          position: "absolute", bottom: "108%", left: "50%",
          transform: `translateX(-50%) rotate(${mapRotation}deg)`,
          whiteSpace: "nowrap", pointerEvents: "none",
          opacity: settings?.nameOpacity ?? 1,
        }}>
          <span style={{
            fontSize: `${dotPx * 0.60}px`, fontWeight: 900, lineHeight: 1,
            color: teamColor,
            textShadow: "0 1px 4px rgba(0,0,0,1), 0 0 8px rgba(0,0,0,0.9)",
            background: "rgba(0,0,0,0.55)",
            padding: `${dotPx * 0.04}px ${dotPx * 0.15}px`,
            borderRadius: `${dotPx * 0.12}px`,
          }}>
            {playerData.m_name}
          </span>
        </div>
      )}

      {/* ── Rotating wrapper — everything inside spins with view angle ── */}
      <div style={{
        width: dotCss, height: dotCss,
        // Context-dependent arrow direction (see arrowRotation above).
        // Full transform chain: mapWrapper[rotate(rotationRef)] →
        //   Player.outer[rotate(-mapRotation)] → Player.inner[rotate(arrowRotation)].
        // Net screen rotation = rotationRef - mapRotation + arrowRotation.
        //   Non-follow:          rotationRef=0, mapRotation=0    → net = -(raw+90) ✓
        //   Follow-rotate centered: arrow=180                    → net = 180°(UP) ✓
        //   Follow-rotate other:   arrow=mapRotation-rotation    → net = mapRotation-(raw+90) ✓
        transform: `rotate(${arrowRotation}deg)`,
        // No transition on rotation — snaps instantly to avoid jitter/wobble
        // when eye angle updates rapidly from backend data.
      }}>

        {/* View cone — tip at player center, fans FORWARD (downward in local space).
            The inner wrapper rotates by arrowRotation, so the on-screen result is
            always the player's forward direction.
            The cone is drawn below center (top:50% + translateX(-50%)) so downward
            = forward in local space, then the rotation wrapper pivots it correctly.
            coneLength setting controls the height multiplier (default 1 = 4× dot size)

            When the player is scoped/zoomed, the in-game FOV narrows (~90° hipfire
            → ~40° scoped), so we narrow the cone width and extend its length to
            represent the longer, tighter line of sight while aiming. */}
        {settings?.showViewCones && !isDead && (() => {
          const scopedNarrow = settings?.scopedConeNarrow !== false;
          const isScopedNow = scopedNarrow && !!getValue(playerData, 'm_is_scoped', ['isScoped', 'is_scoped']);
          const widthMul = settings?.coneWidth ?? 1;
          // Hipfire baseline: 5.0× wide, 4.0× long (×coneLength setting).
          // Scoped: narrow to ~45% width (≈40° FOV) and extend to ~1.8× length.
          const coneWidth = dotPx * 5.0 * widthMul * (isScopedNow ? 0.45 : 1.0);
          const coneHeight = dotPx * 4.0 * (settings?.coneLength ?? 1) * (isScopedNow ? 1.8 : 1.0);
          // Base alpha scaled by the user's cone opacity setting (0..1 → hex).
          const baseAlpha = (isScopedNow ? 0.53 : 0.40) * (settings?.coneOpacity ?? 1);
          const alphaHex = Math.round(Math.max(0, Math.min(1, baseAlpha)) * 255)
            .toString(16).padStart(2, "0");
          return (
            <div style={{
              position: "absolute",
              left: "50%",
              top: "50%",
              width: `${coneWidth}px`,
              height: `${coneHeight}px`,
              transform: "translate(-50%, 0%)",
              clipPath: "polygon(50% 0%, 0% 100%, 100% 100%)",
              background: `linear-gradient(to bottom, ${teamColor}${alphaHex}, ${teamColor}00)`,
              filter: isScopedNow ? "blur(1px)" : "blur(1.5px)",
              transition: "width 0.15s ease, height 0.15s ease",
            }} />
          );
        })()}

        {/* Circle — avatar → character model → colour dot */}
        <div style={{
          position: "absolute", inset: 0,
          borderRadius: "50%",
          overflow: "hidden",
          border: `${dotPx * 0.15}px solid ${teamColor}`,
          boxShadow: isDead
            ? "none"
            : `0 0 0 ${dotPx * 0.05}px rgba(0,0,0,0.8), 0 0 ${dotPx * 3}px ${teamColor}55`,
          background: "#0a0e1a",
          zIndex: 2,
        }}>
          {/* Steam avatar from backend */}
          {avatarUrl && (
            <img
              src={avatarUrl}
              alt=""
              style={{
                width: "100%", height: "100%",
                objectFit: "cover",
                filter: isDead ? "grayscale(100%) brightness(0.4)" : "none",
              }}
              onError={(e) => { e.currentTarget.style.display = "none"; }}
            />
          )}

          {/* Fallback: character model */}
          {!avatarUrl && playerData.m_model_name && (
            <img
              src={`./assets/characters/${playerData.m_model_name}.png`}
              alt=""
              style={{
                position: "absolute", inset: 0,
                width: "100%", height: "100%",
                objectFit: "cover", objectPosition: "top center",
                transform: "scale(1.8) translateY(-10%)",
                transformOrigin: "top center",
                filter: isDead ? "grayscale(100%) brightness(0.4)" : "saturate(0.9)",
              }}
              onError={(e) => { e.currentTarget.style.display = "none"; }}
            />
          )}

          {/* Fallback: colour fill */}
          {!avatarUrl && !playerData.m_model_name && (
            <div style={{
              position: "absolute", inset: 0,
              background: `radial-gradient(circle at 40% 35%, ${teamColor}cc, ${teamColor}66)`,
            }} />
          )}
        </div>

        {/* Direction arrow — sits below circle edge, tip pointing forward.
            borderTop creates a downward-pointing triangle = correct forward arrow. */}
        {!isDead && (
          <div style={{
            position: "absolute",
            bottom: "-30%",
            left: "50%",
            transform: "translateX(-50%)",
            width: 0,
            height: 0,
            borderLeft: `${dotPx * 0.24}px solid transparent`,
            borderRight: `${dotPx * 0.24}px solid transparent`,
            borderTop: `${dotPx * 0.40}px solid ${teamColor}`,
            filter: `drop-shadow(0 0 3px ${teamColor})`,
            zIndex: 3,
          }} />
        )}

      </div>{/* end rotating wrapper */}

      {/* Health bar — outside rotating wrapper so it stays horizontal */}
      {!isDead && playerData.m_health < 100 && (
        <div style={{
          position: "absolute", top: "110%", left: "50%",
          transform: "translateX(-50%)",
          width: "130%", height: 2,
          background: "rgba(0,0,0,0.7)",
          borderRadius: 1,
          overflow: "hidden",
        }}>
          <div style={{
            height: "100%",
            width: `${playerData.m_health}%`,
            background: playerData.m_health > 50 ? "#22c55e"
              : playerData.m_health > 25 ? "#eab308" : "#ef4444",
            transition: "width 0.3s",
          }} />
        </div>
      )}
    </div>
  );
};

export default Player;