import * as THREE from "three";
import { CANVAS_WIDTH, CANVAS_HEIGHT } from "./game.config";
import { GameWorld } from "./GameWorld";
import { Player } from "./Player";
import { ObstacleManager } from "./ObstacleManager";
import { UIManager } from "./UIManager";
import { SoundManager } from "./SoundManager";
import { MobileControls } from "./MobileControls";
import { GameState, THEMES, RemixEffect } from "./types";

// ─── Scene Setup ─────────────────────────────────────────────────────────────
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x080010);
scene.fog = new THREE.FogExp2(0x080010, 0.028); // slightly less fog = more visibility at speed

const aspect = CANVAS_WIDTH / CANVAS_HEIGHT;
const camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 500); // wider FOV

const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance" });
renderer.setSize(CANVAS_WIDTH, CANVAS_HEIGHT);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

const container = document.getElementById("game-container")!;
container.style.width  = `${CANVAS_WIDTH}px`;
container.style.height = `${CANVAS_HEIGHT}px`;
container.appendChild(renderer.domElement);

// ─── Lighting ────────────────────────────────────────────────────────────────
const ambientLight = new THREE.AmbientLight(0x111122, 1.0);
scene.add(ambientLight);

const dirLight = new THREE.DirectionalLight(0xffffff, 0.6);
dirLight.position.set(5, 10, -5);
dirLight.castShadow = true;
dirLight.shadow.mapSize.set(512, 512);
scene.add(dirLight);

const rimLight = new THREE.DirectionalLight(0xff00ff, 0.35);
rimLight.position.set(-5, 3, 5);
scene.add(rimLight);

// ─── Game Systems ─────────────────────────────────────────────────────────────
const sound     = new SoundManager();
const ui        = new UIManager();
const world     = new GameWorld(scene);
const player    = new Player(scene, camera);
const obstacles = new ObstacleManager(scene);

player.onJump = () => sound.playJump();
player.onDash = () => sound.playDash();

// ─── Input ───────────────────────────────────────────────────────────────────
const keys: Record<string, boolean> = {};

// Mobile controls — shares the same keys object
const mobileControls = new MobileControls(keys);
void mobileControls; // referenced for side-effects

// ─── State ────────────────────────────────────────────────────────────────────
let state: GameState = "menu";
let score = 0;
let hp = 3;
let combo = 0;
let comboTimer = 0;
let totalDistance = 0;
let cartridgesCollected = 0;
let survivalTime = 0;
let remixTimer = 0;
const REMIX_INTERVAL = 28;
let activeTheme = "Retro x Space";
let pendingRemix = false;
let shakeTimer = 0;
let shakeIntensity = 0;
let invincibleTimer = 0;

// ── Remix effect state ────────────────────────────────────────────────────────
let activeRemixEffect: RemixEffect | null = null;
let remixEffectTimer = 0;        // seconds remaining
let scoreMultiplier = 1;         // 1 normally, 2 for frenzy, 3 for slow_motion
let worldSpeedMultiplier = 1.0;  // 1 normally, 0.7 for slow_motion, 1.35 for frenzy

window.addEventListener("keydown", (e) => {
  const k = e.key.toLowerCase();
  keys[k] = true;

  if (k === "m") {
    const nowMuted = !sound.isMuted();
    sound.setMuted(nowMuted);
    if (nowMuted) {
      sound.stopEngine();
      sound.stopBGM();
    } else if (state === "playing") {
      sound.startEngine();
      sound.startBGM();
    }
    ui.updateHUD(score, hp, combo, nowMuted, activeRemixEffect, remixEffectTimer);
  }

  if ([", "arrowup", "arrowdown", "arrowleft", "arrowright"].includes(k)) {
    e.preventDefault();
  }
});
window.addEventListener("keyup", (e) => { keys[e.key.toLowerCase()] = false; });

const clock = new THREE.Clock();

// ─── Game Functions ───────────────────────────────────────────────────────────
function startGame() {
  score = 0;
  hp = 3;
  combo = 0;
  comboTimer = 0;
  totalDistance = 0;
  cartridgesCollected = 0;
  survivalTime = 0;
  remixTimer = 0;
  shakeTimer = 0;
  invincibleTimer = 0;
  pendingRemix = false;
  activeTheme = "Retro x Space";
  activeRemixEffect = null;
  remixEffectTimer = 0;
  scoreMultiplier = 1;
  worldSpeedMultiplier = 1.0;

  // Reset obstacle remix flags
  obstacles.setMagnetActive(false);
  obstacles.setShieldActive(false);
  obstacles.setDoubleCartridgesActive(false);
  obstacles.setSpawnRateMultiplier(1.0);

  player.reset();
  world.reset();
  obstacles.reset();
  ui.hideAll();
  state = "playing";
  ui.updateHUD(score, hp, combo, sound.isMuted(), null, 0);

  sound.stopEngine();
  sound.stopBGM();
  sound.startEngine();
  sound.startBGM();
}

function triggerScreenShake(intensity: number, duration: number) {
  shakeIntensity = intensity;
  shakeTimer = duration;
}

function takeDamage() {
  if (invincibleTimer > 0) return;
  hp--;
  invincibleTimer = 1.5;
  triggerScreenShake(0.3, 0.45);
  sound.playDamage();
  ui.flashDamage();
  ui.updateHUD(score, hp, combo, sound.isMuted(), activeRemixEffect, remixEffectTimer);
  if (hp <= 0) endGame();
}

function collectCartridge(points: number) {
  combo++;
  comboTimer = 3.0;
  const gained = Math.floor(points * Math.max(1, combo) * scoreMultiplier);
  score += gained;
  cartridgesCollected++;
  sound.playCollect(combo);
  ui.showComboPopup(combo, gained);
  ui.updateHUD(score, hp, combo, sound.isMuted(), activeRemixEffect, remixEffectTimer);
  triggerScreenShake(0.05, 0.1);
}

function endGame() {
  state = "gameover";
  sound.stopEngine();
  sound.stopBGM();
  sound.playGameOver();
  // Clear any active remix effects
  clearRemixEffect();
  const survivalBonus = Math.floor(survivalTime * 10);
  score += survivalBonus;
  ui.showGameOver(score, cartridgesCollected, combo, activeTheme, survivalBonus);
}

function clearRemixEffect() {
  activeRemixEffect = null;
  remixEffectTimer = 0;
  scoreMultiplier = 1;
  worldSpeedMultiplier = 1.0;
  obstacles.setMagnetActive(false);
  obstacles.setShieldActive(false);
  obstacles.setDoubleCartridgesActive(false);
  obstacles.setSpawnRateMultiplier(1.0);
}

function activateRemixEffect(effect: RemixEffect) {
  clearRemixEffect();
  activeRemixEffect = effect;

  switch (effect) {
    case "double_cartridges":
      remixEffectTimer = 30;
      obstacles.setDoubleCartridgesActive(true);
      obstacles.setSpawnRateMultiplier(1.4);
      scoreMultiplier = 1;
      worldSpeedMultiplier = 1.0;
      break;
    case "magnet":
      remixEffectTimer = 30;
      obstacles.setMagnetActive(true);
      scoreMultiplier = 1;
      worldSpeedMultiplier = 1.0;
      break;
    case "shield":
      remixEffectTimer = 20;
      obstacles.setShieldActive(true);
      scoreMultiplier = 1;
      worldSpeedMultiplier = 1.0;
      break;
    case "slow_motion":
      remixEffectTimer = 30;
      scoreMultiplier = 3;
      worldSpeedMultiplier = 0.7;
      break;
    case "frenzy":
      remixEffectTimer = 25;
      scoreMultiplier = 2;
      worldSpeedMultiplier = 1.4;
      break;
  }
}

function applyRemixCard(theme: string) {
  activeTheme = theme;
  world.applyTheme(theme);
  obstacles.applyTheme(theme);
  sound.playRemixPick();
  pendingRemix = false;
  state = "playing";
  ui.hideRemixCards();
  sound.startEngine();
  sound.startBGM();

  // Activate the gameplay effect tied to this theme
  const themeConfig = THEMES.find(t => t.name === theme);
  if (themeConfig) {
    activateRemixEffect(themeConfig.remixEffect);
    ui.showRemixEffectBanner(themeConfig.remixEffectLabel, themeConfig.remixEffectDesc);
  }

  ui.updateHUD(score, hp, combo, sound.isMuted(), activeRemixEffect, remixEffectTimer);
}

window.addEventListener("dc-restart", () => startGame());

// ─── Main Loop ────────────────────────────────────────────────────────────────
function animate() {
  requestAnimationFrame(animate);
  const dt = Math.min(clock.getDelta(), 0.05);

  if (state === "menu") {
    world.update(dt, 8); // faster menu scroll
    player.updateIdle(dt);
    camera.position.set(0, 3.5, 8);
    camera.lookAt(0, 1, 0);
    renderer.render(scene, camera);
    return;
  }

  if (state === "gameover" || state === "remix") {
    renderer.render(scene, camera);
    return;
  }

  if (state !== "playing") {
    renderer.render(scene, camera);
    return;
  }

  // ── Playing ──
  survivalTime += dt;
  remixTimer   += dt;

  // ── Remix effect countdown ────────────────────────────────────────────────
  if (activeRemixEffect && remixEffectTimer > 0) {
    remixEffectTimer -= dt;
    if (remixEffectTimer <= 0) {
      clearRemixEffect();
      ui.updateHUD(score, hp, combo, sound.isMuted(), null, 0);
    } else {
      if (Math.floor(remixEffectTimer * 2) !== Math.floor((remixEffectTimer + dt) * 2)) {
        ui.updateHUD(score, hp, combo, sound.isMuted(), activeRemixEffect, remixEffectTimer);
      }
    }
  }

  if (comboTimer > 0) {
    comboTimer -= dt;
    if (comboTimer <= 0 && combo > 0) {
      combo = 0;
      ui.updateHUD(score, hp, combo, sound.isMuted(), activeRemixEffect, remixEffectTimer);
    }
  }

  if (invincibleTimer > 0) invincibleTimer -= dt;

  if (remixTimer >= REMIX_INTERVAL && !pendingRemix) {
    pendingRemix = true;
    remixTimer = 0;
    state = "remix";
    sound.stopEngine();
    sound.stopBGM();
    sound.playRemixAppear();
    ui.showRemixCards(applyRemixCard);
  }

  // Apply world speed multiplier (slow_motion / frenzy)
  const baseSpeed = world.getSpeed();
  const effectiveSpeed = baseSpeed * worldSpeedMultiplier;

  totalDistance += effectiveSpeed * dt;

  player.update(dt, keys, effectiveSpeed);
  world.update(dt, effectiveSpeed);
  sound.updateEngine(effectiveSpeed);

  const playerBounds      = player.getBounds();
  const playerLane        = player.getLane();
  const playerY           = player.getPositionY();
  const isDashInvincible  = player.isDashInvincible();
  const playerPos         = player.getPosition();

  const results = obstacles.update(
    dt, effectiveSpeed, totalDistance,
    playerBounds, playerLane, playerY,
    isDashInvincible, playerPos
  );

  for (const r of results) {
    if (r.type === "cartridge") collectCartridge(r.points ?? 100);
    if (r.type === "damage")    takeDamage();
  }

  // Survival score trickle — multiplied by score multiplier, scales with speed
  if (Math.floor(survivalTime) > Math.floor(survivalTime - dt)) {
    score += Math.floor(8 * scoreMultiplier); // increased from 5
    ui.updateHUD(score, hp, combo, sound.isMuted(), activeRemixEffect, remixEffectTimer);
  }

  player.updateCamera(dt, shakeTimer > 0 ? shakeIntensity : 0);
  if (shakeTimer > 0) shakeTimer -= dt;

  renderer.render(scene, camera);
}

// ─── Kickoff ──────────────────────────────────────────────────────────────────
ui.showMenu(startGame);
animate();
