const DatapackLoaderRef = require("./DatapackLoader");
const CombatServiceRef = require("./CombatService");
const QuestsManager = require("./QuestsManager");
const { Player } = require("../models");

class DungeonManager {
  constructor() {
    this.activeSessions = new Map();
  }

  async startDungeon(playerId, dungeonId) {
    const dungeon = DatapackLoaderRef.getDungeon(dungeonId);
    if (!dungeon || !dungeon.rooms?.length) return null;

    const player = await Player.findByPk(playerId);
    const session = {
      playerId,
      dungeonId,
      currentRoomIndex: 0,
      isFinished: false,
      battle: null,
      rewards: { xp: 0, credits: 0, items: [] },
    };

    this.activeSessions.set(playerId, session);
    return this.initRoom(playerId, player);
  }

  async initRoom(playerId, playerInstance = null) {
    const session = this.activeSessions.get(playerId);
    if (!session) return null;
    const roomData = this.getCurrentRoomData(playerId);
    const player = playerInstance || (await Player.findByPk(playerId));

    if (roomData.hostiles.length > 0) {
      session.battle = CombatServiceRef.initializeBattle(
        player,
        roomData.hostiles,
      );
    } else {
      session.battle = null;
    }
    return { ...roomData, battle: session.battle };
  }

  getCurrentRoomData(playerId) {
    const session = this.activeSessions.get(playerId);
    const dungeon = DatapackLoaderRef.getDungeon(session.dungeonId);
    const roomRef = dungeon.rooms[session.currentRoomIndex];
    const rawRoom = DatapackLoaderRef.getRoom(roomRef.id);
    const hostiles = (rawRoom.hostiles || [])
      .map((hId) => DatapackLoaderRef.getEnemy(hId))
      .filter(Boolean);

    return {
      roomIndex: session.currentRoomIndex,
      totalRooms: dungeon.rooms.length,
      config: rawRoom,
      hostiles,
    };
  }

  processCombatAction(playerId, targetInstanceId, socket = null) {
    const session = this.activeSessions.get(playerId);
    if (!session || !session.battle || session.battle.isOver) return null;

    const battle = session.battle;
    if (battle.turnOrder[battle.currentTurnIndex] !== "player") return null;

    let logMessages;
    if (!targetInstanceId) {
      logMessages = CombatServiceRef.handleSkip(battle);
    } else {
      logMessages = CombatServiceRef.handleAttack(battle, targetInstanceId);
    }

    const playerAction = {
      attackerId: "player",
      messages: logMessages,
      hpState: this._captureHpState(battle),
    };

    return this._afterAction(session, [playerAction], socket);
  }

  _captureHpState(battle) {
    return {
      playerHp: battle.player.hp,
      enemies: battle.enemies.map((e) => ({
        id: e.instanceId,
        hp: e.hp,
        isDead: e.isDead,
      })),
    };
  }

  _afterAction(session, actionLogs, socket) {
    const battle = session.battle;

    battle.enemies.forEach((enemy) => {
      if (enemy.isDead && !enemy.rewardGiven) {
        enemy.rewardGiven = true;
        session.rewards.xp += enemy.gainXp || 0;
        session.rewards.credits += enemy.credits || 0;

        if (enemy.loot && Array.isArray(enemy.loot)) {
          const lootMessages = this._distributeLoot(session, enemy.loot);
          actionLogs.push(...lootMessages);
        }

        QuestsManager.trackProgress(
          session.playerId,
          "KILL_ENEMY",
          enemy.id,
          1,
          socket,
        );
      }
    });

    const allEnemiesDead = battle.enemies.every((e) => e.isDead);

    if (allEnemiesDead) {
      battle.isOver = true;
      const roomConfig = this.getCurrentRoomData(session.playerId).config;
      if (roomConfig && !roomConfig.rewardGiven) {
        roomConfig.rewardGiven = true;
        session.rewards.xp += roomConfig.gainXp || 0;
        session.rewards.credits += roomConfig.credits || 0;
        if (roomConfig.loot) this._distributeLoot(session, roomConfig.loot);
      }
      return { battle, log: actionLogs, status: "victory" };
    }

    if (battle.player.hp <= 0) {
      battle.isOver = true;
      return { battle, log: actionLogs, status: "defeat" };
    }

    return this._nextTurn(session, actionLogs);
  }

  _distributeLoot(session, lootTable) {
    const rewardsLog = [];
    lootTable.forEach((item) => {
      if (Math.random() <= (item.chance || 1)) {
        let finalCount = 0;
        if (typeof item.count === "object" && item.count !== null) {
          const min = item.count.min || 0;
          const max = item.count.max || 1;
          finalCount = Math.floor(Math.random() * (max - min + 1)) + min;
        } else {
          finalCount = Number(item.count) || 1;
        }

        if (finalCount > 0) {
          const existing = session.rewards.items.find((i) => i.id === item.id);
          if (existing) {
            existing.count += finalCount;
          } else {
            session.rewards.items.push({ id: item.id, count: finalCount });
          }
          const shortName = item.id.split(":").pop().replace("_", " ");
          rewardsLog.push(
            `RECOVERED: ${finalCount}x ${shortName.toUpperCase()}`,
          );
        }
      }
    });
    return rewardsLog;
  }

  _nextTurn(session, accumulatedLogs = []) {
    const battle = session.battle;
    battle.currentTurnIndex =
      (battle.currentTurnIndex + 1) % battle.turnOrder.length;
    battle.turnStartTime = Date.now();

    const currentId = battle.turnOrder[battle.currentTurnIndex];
    if (currentId === "player") return { battle, log: accumulatedLogs };

    const enemy = battle.enemies.find((e) => e.instanceId === currentId);
    if (!enemy || enemy.isDead) return this._nextTurn(session, accumulatedLogs);

    const enemyMessages = CombatServiceRef.handleAttack(battle, null);
    accumulatedLogs.push({
      attackerId: currentId,
      messages: enemyMessages,
      hpState: this._captureHpState(battle),
    });

    if (battle.player.hp <= 0 || battle.enemies.every((e) => e.isDead)) {
      return this._afterAction(session, accumulatedLogs);
    }

    return this._nextTurn(session, accumulatedLogs);
  }

  async moveToNextRoom(playerId) {
    const session = this.activeSessions.get(playerId);
    if (!session) return { error: "Session not found" };

    if (session.isFinished) {
      return { status: "completed", rewards: session.rewards };
    }

    const dungeon = DatapackLoaderRef.getDungeon(session.dungeonId);

    if (session.currentRoomIndex < dungeon.rooms.length - 1) {
      session.currentRoomIndex++;
      const newRoomData = await this.initRoom(playerId);
      return { status: "next_room", ...newRoomData };
    } else {
      session.isFinished = true;
      return { status: "completed", rewards: session.rewards };
    }
  }

  leaveDungeon(playerId) {
    this.activeSessions.delete(playerId);
  }
}

module.exports = new DungeonManager();
