// Reading types.ts to verify actor structure before next step
import Phaser from "phaser";
import {
  type EntityId,
  type Vec2,
  type Action,
  type RunState,
  type World,
  type CombatantActor,
  type UIUpdatePayload
} from "../core/types";
import { TILE_SIZE } from "../core/constants";
import { inBounds, isBlocked, isPlayerOnExit, tryDestructTile } from "../engine/world/world-logic";
import { findPathAStar } from "../engine/world/pathfinding";
import { applyAction, stepUntilPlayerTurn } from "../engine/simulation/simulation";
import { generateWorld } from "../engine/world/generator";
import { DungeonRenderer } from "../rendering/DungeonRenderer";
import { GAME_CONFIG } from "../core/config/GameConfig";
import { EntityManager } from "../engine/EntityManager";
import { ProgressionManager } from "../engine/ProgressionManager";
import GameUI from "../ui/GameUI";
import { CameraController } from "./systems/CameraController";
import { ItemManager } from "./systems/ItemManager";
import { TargetingSystem } from "./systems/TargetingSystem";

export class GameScene extends Phaser.Scene {
  private world!: World;
  private playerId!: EntityId;

  private floorIndex = 1;

  private runState: RunState = {
    stats: { ...GAME_CONFIG.player.initialStats },
    inventory: { gold: 0, items: [] }
  };

  private cursors!: Phaser.Types.Input.Keyboard.CursorKeys;

  private playerPath: Vec2[] = [];
  private awaitingPlayer = false;

  // Sub-systems
  private dungeonRenderer!: DungeonRenderer;
  private cameraController!: CameraController;
  private itemManager!: ItemManager;
  private isMenuOpen = false;
  private isInventoryOpen = false;
  private isCharacterOpen = false;

  private entityManager!: EntityManager;
  private progressionManager: ProgressionManager = new ProgressionManager();
  private targetingSystem!: TargetingSystem;

  private turnCount = 0; // Track turns for mana regen

  constructor() {
    super("GameScene");
  }

  create() {
    this.cursors = this.input.keyboard!.createCursorKeys();

    // Camera
    this.cameras.main.setZoom(GAME_CONFIG.rendering.cameraZoom);
    this.cameras.main.fadeIn(1000, 0, 0, 0);

    // Initialize Sub-systems
    this.dungeonRenderer = new DungeonRenderer(this); 
    this.cameraController = new CameraController(this.cameras.main);
    this.itemManager = new ItemManager(this.world, this.entityManager);
    const targetingGraphics = this.add.graphics().setDepth(2000);
    this.targetingSystem = new TargetingSystem(targetingGraphics);

    // Launch UI Scene
    this.scene.launch("GameUI");

    // Listen for Menu State
    this.events.on("menu-toggled", (isOpen: boolean) => {
      this.isMenuOpen = isOpen;
    });
    this.events.on("inventory-toggled", (isOpen: boolean) => {
      this.isInventoryOpen = isOpen;
    });
    this.events.on("character-toggled", (isOpen: boolean) => {
      this.isCharacterOpen = isOpen;
    });

    // Load initial floor
    this.loadFloor(1);

    // Menu Inputs
    this.input.keyboard?.on("keydown-I", () => {
      if (this.dungeonRenderer.isMinimapVisible()) {
        this.dungeonRenderer.toggleMinimap();
      }
      this.events.emit("toggle-menu");
      this.emitUIUpdate();
    });
    this.input.keyboard?.on("keydown-ESC", () => {
      this.events.emit("close-menu");
      if (this.dungeonRenderer.isMinimapVisible()) {
        this.dungeonRenderer.toggleMinimap();
      }
    });
    this.input.keyboard?.on("keydown-M", () => {
      this.events.emit("close-menu");
      this.dungeonRenderer.toggleMinimap();
    });
    this.input.keyboard?.on("keydown-B", () => {
      this.events.emit("toggle-inventory");
    });
    this.input.keyboard?.on("keydown-C", () => {
      this.events.emit("toggle-character");
    });

    this.input.keyboard?.on("keydown-SPACE", () => {
      if (!this.awaitingPlayer) return;
      if (this.isMenuOpen || this.isInventoryOpen || this.dungeonRenderer.isMinimapVisible()) return;
      this.commitPlayerAction({ type: "wait" });
    });

    // Listen for Map button click from UI
    this.events.on("toggle-minimap", () => {
      this.dungeonRenderer.toggleMinimap();
    });

    // Listen for UI update requests
    this.events.on("request-ui-update", () => {
      this.emitUIUpdate();
    });

    // Listen for game restart
    this.events.on("restart-game", () => {
      this.restartGame();
    });

    this.events.on("allocate-stat", (statName: string) => {
        const player = this.world.actors.get(this.playerId) as CombatantActor;
        if (player) {
            this.progressionManager.allocateStat(player, statName);
             this.emitUIUpdate();
         }
    });

    this.events.on("allocate-passive", (nodeId: string) => {
        const player = this.world.actors.get(this.playerId) as CombatantActor;
        if (player) {
            this.progressionManager.allocatePassive(player, nodeId);
             this.emitUIUpdate();
         }
    });

    this.events.on("player-wait", () => {
        if (!this.awaitingPlayer) return;
        if (this.isMenuOpen || this.isInventoryOpen || this.dungeonRenderer.isMinimapVisible()) return;
        this.commitPlayerAction({ type: "wait" });
    });

    this.events.on("player-search", () => {
        if (!this.awaitingPlayer) return;
        if (this.isMenuOpen || this.isInventoryOpen || this.dungeonRenderer.isMinimapVisible()) return;
        
        console.log("Player searching...");
        this.commitPlayerAction({ type: "wait" });
    });

    this.events.on("use-item", (data: { itemId: string }) => {
        if (!this.awaitingPlayer) return;
        
        const player = this.world.actors.get(this.playerId) as CombatantActor;
        if (!player || !player.inventory) return;

        const itemIdx = player.inventory.items.findIndex(it => it.id === data.itemId);
        if (itemIdx === -1) return;
        const item = player.inventory.items[itemIdx];

        const result = this.itemManager.handleUse(data.itemId, player);
        
        if (result.success && result.consumed) {
            const healAmount = player.stats.maxHp - player.stats.hp; // Already healed by manager
            const actualHeal = Math.min(healAmount, player.stats.hp);
            this.dungeonRenderer.showHeal(player.pos.x, player.pos.y, actualHeal);
            this.commitPlayerAction({ type: "wait" }); 
            this.emitUIUpdate();
        } else if (result.success && !result.consumed) {
             // Throwable item - start targeting
             if (this.targetingSystem.isActive && this.targetingSystem.itemId === item.id) {
                 // Already targeting - execute throw
                 if (this.targetingSystem.cursorPos) {
                     this.executeThrow(this.targetingSystem.cursorPos.x, this.targetingSystem.cursorPos.y);
                 }
                 return;
             }
             
             this.targetingSystem.startTargeting(
                 item.id,
                 player.pos,
                 this.world,
                 this.dungeonRenderer.seenArray,
                 this.world.width
             );
             this.emitUIUpdate();
        }
    });

    // Right Clicks to cancel targeting
    this.input.on('pointerdown', (p: Phaser.Input.Pointer) => {
        if (p.rightButtonDown() && this.targetingSystem.isActive) {
            this.targetingSystem.cancel();
            this.emitUIUpdate();
        }
    });

    // Zoom Control
    this.input.on("wheel", (_pointer: Phaser.Input.Pointer, _gameObjects: Phaser.GameObjects.GameObject[], _deltaX: number, deltaY: number, _deltaZ: number) => {
        if (this.isMenuOpen || this.isInventoryOpen || this.dungeonRenderer.isMinimapVisible()) return;
        this.cameraController.handleWheel(deltaY);
    });

    // Disable context menu for right-click panning
    this.input.mouse?.disableContextMenu();

    // Camera Panning
    this.input.on("pointermove", (p: Phaser.Input.Pointer) => {
      if (!p.isDown) { // Even if not down, we might need to update targeting line
        if (this.targetingSystem.isActive) {
             const tx = Math.floor(p.worldX / TILE_SIZE);
             const ty = Math.floor(p.worldY / TILE_SIZE);
             const player = this.world.actors.get(this.playerId) as CombatantActor;
             if (player) {
                 this.targetingSystem.updateCursor({ x: tx, y: ty }, player.pos);
             }
        }
        return;
      }
      
      if (this.isMenuOpen || this.isInventoryOpen || this.dungeonRenderer.isMinimapVisible()) return;

      const isRightDrag = p.rightButtonDown();
      const isMiddleDrag = p.middleButtonDown();
      const isShiftDrag = p.isDown && p.event.shiftKey;

      if (isRightDrag || isMiddleDrag || isShiftDrag) { 
          const { x, y } = p.position;
          const { x: prevX, y: prevY } = p.prevPosition;
          
          const dx = (x - prevX) / this.cameras.main.zoom;
          const dy = (y - prevY) / this.cameras.main.zoom;

          this.cameraController.handlePan(dx, dy);
      }
      
      if (this.targetingSystem.isActive) {
        const tx = Math.floor(p.worldX / TILE_SIZE);
        const ty = Math.floor(p.worldY / TILE_SIZE);
        const player = this.world.actors.get(this.playerId) as CombatantActor;
        if (player) {
            this.targetingSystem.updateCursor({ x: tx, y: ty }, player.pos);
        }
      }
    });

    // Mouse click -> 
    this.input.on("pointerdown", (p: Phaser.Input.Pointer) => {
      // Targeting Click
      if (this.targetingSystem.isActive) {
          // Only Left Click throws
          if (p.button === 0) {
             if (this.targetingSystem.cursorPos) {
                this.executeThrow(this.targetingSystem.cursorPos.x, this.targetingSystem.cursorPos.y);
             }
          }
          return;
      }

      // Movement Click
      if (p.button !== 0) return;
      
      this.cameraController.enableFollowMode();

      if (!this.awaitingPlayer) return;
      if (this.isMenuOpen || this.isInventoryOpen || this.dungeonRenderer.isMinimapVisible()) return;

      const tx = Math.floor(p.worldX / TILE_SIZE);
      const ty = Math.floor(p.worldY / TILE_SIZE);

      if (!inBounds(this.world, tx, ty)) return;

      if (!this.dungeonRenderer.isSeen(tx, ty)) return;

      const isEnemy = [...this.world.actors.values()].some(a => 
         a.category === "combatant" && a.pos.x === tx && a.pos.y === ty && !a.isPlayer
      );

      const player = this.world.actors.get(this.playerId) as CombatantActor;
      const dx = tx - player.pos.x;
      const dy = ty - player.pos.y;
      const isDiagonalNeighbor = Math.abs(dx) === 1 && Math.abs(dy) === 1;

      if (isEnemy && isDiagonalNeighbor) {
         const targetId = [...this.world.actors.values()].find(
             a => a.category === "combatant" && a.pos.x === tx && a.pos.y === ty && !a.isPlayer
         )?.id;
         if (targetId !== undefined) {
             this.commitPlayerAction({ type: "attack", targetId });
             return;
         }
      }

       const path = findPathAStar(
           this.world, 
           this.dungeonRenderer.seenArray, 
           { ...player.pos }, 
           { x: tx, y: ty },
           { ignoreBlockedTarget: isEnemy }
       );

      if (path.length >= 2) this.playerPath = path;
      this.dungeonRenderer.render(this.playerPath);
    });
  }

  update() {
    if (!this.awaitingPlayer) return;
    if (this.isMenuOpen || this.isInventoryOpen || this.isCharacterOpen || this.dungeonRenderer.isMinimapVisible()) return;

    // Auto-walk one step per turn
    if (this.playerPath.length >= 2) {
      const player = this.world.actors.get(this.playerId) as CombatantActor;
      const next = this.playerPath[1];
      const dx = next.x - player.pos.x;
      const dy = next.y - player.pos.y;

      if (Math.abs(dx) + Math.abs(dy) !== 1) {
        this.playerPath = [];
        return;
      }

      if (isBlocked(this.world, next.x, next.y, this.entityManager)) {
        const targetId = [...this.world.actors.values()].find(
            a => a.category === "combatant" && a.pos.x === next.x && a.pos.y === next.y && !a.isPlayer
        )?.id;

        if (targetId !== undefined) {
             this.commitPlayerAction({ type: "attack", targetId });
             this.playerPath = []; 
             return;
        } else {
             this.playerPath = [];
             return;
        }
      } 
      
      this.commitPlayerAction({ type: "move", dx, dy });
      this.playerPath.shift();
      return;
    }

    let action: Action | null = null;
    let dx = 0;
    let dy = 0;

    const anyJustDown = Phaser.Input.Keyboard.JustDown(this.cursors.left!) ||
                        Phaser.Input.Keyboard.JustDown(this.cursors.right!) ||
                        Phaser.Input.Keyboard.JustDown(this.cursors.up!) ||
                        Phaser.Input.Keyboard.JustDown(this.cursors.down!);

    if (anyJustDown) {
        dx = 0; dy = 0;
        if (this.cursors.left!.isDown) dx -= 1;
        if (this.cursors.right!.isDown) dx += 1;
        if (this.cursors.up!.isDown) dy -= 1;
        if (this.cursors.down!.isDown) dy += 1;

        if (dx !== 0 || dy !== 0) {
            if (this.targetingSystem.isActive) {
                this.targetingSystem.cancel();
                this.emitUIUpdate();
            }
            const player = this.world.actors.get(this.playerId) as CombatantActor;
            const targetX = player.pos.x + dx;
            const targetY = player.pos.y + dy;
      
            const targetId = [...this.world.actors.values()].find(
              a => a.category === "combatant" && a.pos.x === targetX && a.pos.y === targetY && !a.isPlayer
            )?.id;
      
            if (targetId !== undefined) {
              action = { type: "attack", targetId };
            } else {
               if (Math.abs(dx) + Math.abs(dy) === 1) {
                  action = { type: "move", dx, dy };
               }
            }
        }
    }

    if (action) {
      this.playerPath = [];
      this.commitPlayerAction(action);
    }
  }

  private emitUIUpdate() {
      const payload: UIUpdatePayload = {
          world: this.world,
          playerId: this.playerId,
          floorIndex: this.floorIndex,
          uiState: {
              targetingItemId: this.targetingSystem.itemId
          }
      };
      this.events.emit("update-ui", payload);
  }

  private commitPlayerAction(action: Action) {
    const playerEvents = applyAction(this.world, this.playerId, action, this.entityManager);

    if (playerEvents.some(ev => ev.type === "move-blocked")) {
        return;
    }

    this.awaitingPlayer = false;
    this.cameraController.enableFollowMode();
    
    // Check for pickups right after move (before enemy turn, so you get it efficiently)
    if (action.type === "move") {
        const player = this.world.actors.get(this.playerId) as CombatantActor;
        const pickedItem = this.itemManager.tryPickup(player);
        if (pickedItem) {
            this.emitUIUpdate();
        }
    }

    const enemyStep = stepUntilPlayerTurn(this.world, this.playerId, this.entityManager);
    this.awaitingPlayer = enemyStep.awaitingPlayerId === this.playerId;

    this.turnCount++;
    if (this.turnCount % GAME_CONFIG.mana.regenInterval === 0) {
      const player = this.world.actors.get(this.playerId) as CombatantActor;
      if (player && player.stats.mana < player.stats.maxMana) {
        const regenAmount = Math.min(
          GAME_CONFIG.mana.regenPerTurn,
          player.stats.maxMana - player.stats.mana
        );
        player.stats.mana += regenAmount;
      }
    }


    const allEvents = [...playerEvents, ...enemyStep.events];
    for (const ev of allEvents) {
      if (ev.type === "damaged") {
         this.dungeonRenderer.showDamage(ev.x, ev.y, ev.amount, ev.isCrit, ev.isBlock);
      } else if (ev.type === "dodged") {
         this.dungeonRenderer.showDodge(ev.x, ev.y);
      } else if (ev.type === "healed") {
         this.dungeonRenderer.showHeal(ev.x, ev.y, ev.amount);
      } else if (ev.type === "killed") {
         this.dungeonRenderer.spawnCorpse(ev.x, ev.y, ev.victimType || "rat");
      } else if (ev.type === "waited" && ev.actorId === this.playerId) {
         const player = this.world.actors.get(this.playerId) as CombatantActor;
         if (player) {
           this.dungeonRenderer.showWait(player.pos.x, player.pos.y);
         }
      } else if (ev.type === "orb-spawned") {
         this.dungeonRenderer.spawnOrb(ev.orbId, ev.x, ev.y);
      } else if (ev.type === "exp-collected" && ev.actorId === this.playerId) {
          this.dungeonRenderer.collectOrb(ev.actorId, ev.amount, ev.x, ev.y);
       } else if (ev.type === "leveled-up" && ev.actorId === this.playerId) {
          this.dungeonRenderer.showLevelUp(ev.x, ev.y);
       } else if (ev.type === "enemy-alerted") {
          this.dungeonRenderer.showAlert(ev.x, ev.y);
       }
     }


    if (!this.world.actors.has(this.playerId)) {
      this.syncRunStateFromPlayer(); 
      const uiScene = this.scene.get("GameUI") as GameUI;
      if (uiScene && 'showDeathScreen' in uiScene) {
        uiScene.showDeathScreen({
          floor: this.floorIndex,
          gold: this.runState.inventory.gold,
          stats: this.runState.stats
        });
      }
      return;
    }

    if (isPlayerOnExit(this.world, this.playerId)) {
      this.syncRunStateFromPlayer();
      this.floorIndex++;
      this.loadFloor(this.floorIndex);
      return;
    }

    this.dungeonRenderer.computeFov(this.playerId);
    if (this.cameraController.isFollowing) {
      const player = this.world.actors.get(this.playerId) as CombatantActor;
      this.cameraController.centerOnTile(player.pos.x, player.pos.y);
    }
    this.dungeonRenderer.render(this.playerPath);
    this.emitUIUpdate();
  }

  private loadFloor(floor: number) {
    this.floorIndex = floor;
    this.cameraController.enableFollowMode();

    const { world, playerId } = generateWorld(floor, this.runState);
    this.world = world;
    this.playerId = playerId;
    this.entityManager = new EntityManager(this.world);
    this.itemManager.updateWorld(this.world, this.entityManager);


    this.playerPath = [];
    this.awaitingPlayer = false;

    this.cameraController.setBounds(0, 0, this.world.width * TILE_SIZE, this.world.height * TILE_SIZE);

    this.dungeonRenderer.initializeFloor(this.world, this.playerId);

    const enemyStep = stepUntilPlayerTurn(this.world, this.playerId, this.entityManager);
    this.awaitingPlayer = enemyStep.awaitingPlayerId === this.playerId;


    this.dungeonRenderer.computeFov(this.playerId);
    const player = this.world.actors.get(this.playerId) as CombatantActor;
    this.cameraController.centerOnTile(player.pos.x, player.pos.y);
    this.dungeonRenderer.render(this.playerPath);
    this.emitUIUpdate();
    
    // Create daggers for testing if none exist (redundant if generator does it, but good for safety)
    // Removed to rely on generator.ts
  }

  private syncRunStateFromPlayer() {
    const p = this.world.actors.get(this.playerId) as CombatantActor;
    if (!p || p.category !== "combatant" || !p.stats || !p.inventory) return;

    this.runState = {
      stats: { ...p.stats },
      inventory: { gold: p.inventory.gold, items: [...p.inventory.items] }
    };
  }

  private restartGame() {
    this.runState = {
      stats: { ...GAME_CONFIG.player.initialStats },
      inventory: { gold: 0, items: [] }
    };
    this.floorIndex = 1;
    this.loadFloor(this.floorIndex);
  }





  private executeThrow(_targetX: number, _targetY: number) {
      const success = this.targetingSystem.executeThrow(
          this.world,
          this.playerId,
          this.entityManager,
          (blockedPos, hitActorId, item) => {
              if (hitActorId !== undefined) {
                  const victim = this.world.actors.get(hitActorId) as CombatantActor;
                  if (victim) {
                      const dmg = item.stats?.attack ?? 1;
                      victim.stats.hp -= dmg;
                      this.dungeonRenderer.showDamage(victim.pos.x, victim.pos.y, dmg);
                      this.dungeonRenderer.shakeCamera();
                  }
              }
              
              const player = this.world.actors.get(this.playerId) as CombatantActor;
              this.dungeonRenderer.showProjectile(
                  player.pos,
                  blockedPos,
                  item.id,
                  () => {
                      // Drop the actual item at the landing spot
                      this.itemManager.spawnItem(item, blockedPos);

                      // Trigger destruction/interaction
                      if (tryDestructTile(this.world, blockedPos.x, blockedPos.y)) {
                          this.dungeonRenderer.updateTile(blockedPos.x, blockedPos.y);
                      }

                      this.targetingSystem.cancel();
                      this.commitPlayerAction({ type: "throw" });
                      this.emitUIUpdate();
                  }
              );
          }
      );

      if (!success) {
          this.emitUIUpdate();
      }
  }

}
