import type { Database } from "sqlite";
import { compileSonusSkills, RawSonusSkill } from "../../src/features/battle/skillCompiler";
import { BattleElement, BattleEntity, InstrumentFamily, SkillData } from "../../src/features/battle/types/battleTypes";

export interface TowerBattlePayload {
  simulationId: string;
  playerId: string;
  context: Record<string, unknown>;
  encounter: Record<string, unknown>;
  allies: BattleEntity[];
  enemies: BattleEntity[];
  battleEngine: "features/battle";
}

const normalizeElement = (raw: unknown): BattleElement => {
  const element = String(raw ?? "").trim().toLowerCase();
  if (element === "ignis" || element === "fire") return "Ignis";
  if (element === "aqua" || element === "water") return "Aqua";
  if (element === "ventus" || element === "wind") return "Ventus";
  if (element === "terra" || element === "earth") return "Terra";
  if (element === "umbra" || element === "dark") return "Umbra";
  return "Lux";
};

const basicSkill: SkillData = {
  id: 0,
  phase: 0,
  name: "Golpe de Resonancia",
  description: "Ataque básico de calibración.",
  type: "Ataque Básico",
  cooldown: 0,
  cost: 0,
  target: "enemy",
  effects: [
    {
      type: "damage",
      value: 1,
      target: "enemy",
    },
  ],
  element: "Lux",
};

const resolveFamily = (instrument: unknown): InstrumentFamily => {
  const value = String(instrument ?? "").trim().toLowerCase();
  if (/arpa|lira|mandolina|la[uú]d|guitarra|viol[ií]n|violonchelo/.test(value)) return InstrumentFamily.Cuerdas;
  if (/flauta|oboe|clarinete|ocarina|sax|flaut[ií]n|carill[ió]n|xil[ió]fono/.test(value)) return InstrumentFamily.Teclados;
  if (/trompeta|tromb[oó]n|tuba|trompa|fagot|corn[oó]/.test(value)) return InstrumentFamily.Metales;
  if (/bombo|caj[oó]n|tambor|percusi[oó]n|pandero/.test(value)) return InstrumentFamily.Percusion;
  return InstrumentFamily.Viento;
};

const makeBasicSkill = (element: BattleElement): SkillData => ({
  ...basicSkill,
  element,
});

const mapSkills = (rows: any[], owner?: { element?: string; nombre?: string }) => {
  const sourceSkills: RawSonusSkill[] = rows.map((row) => ({
    phase: Number(row.fase ?? 1),
    name: String(row.nombre ?? "Habilidad"),
    description: String(row.descripcion ?? ""),
    effect: String(row.efecto ?? row.descripcion ?? ""),
    spritePath: row.sprite_path ?? undefined,
  }));

  const compiled = compileSonusSkills(sourceSkills, owner as any);
  return compiled.length > 0 ? compiled : [makeBasicSkill(normalizeElement(owner?.element))];
};

const makeBattleEntity = (
  payload: {
    idPrefix: string;
    index: number;
    sonusId: number;
    name: string;
    element: string;
    instrument: string | null;
    hp: number;
    atk: number;
    def: number;
    spd: number;
    rarity?: string | null;
    isEnemy: boolean;
    skills: SkillData[];
  }
): BattleEntity => {
  const maxHp = Math.max(1, Math.round(payload.hp));
  const maxBrillo = Math.max(25, Math.round(payload.spd * 1.2 + 20));

  return {
    id: `${payload.idPrefix}-${payload.sonusId}-${payload.index}`,
    dbId: payload.sonusId,
    name: payload.name,
    evolutionLevel: 0,
    battlePose: "idle",
    poseUntil: undefined,
    family: resolveFamily(payload.instrument),
    element: normalizeElement(payload.element),
    isEnemy: payload.isEnemy,
    slot: payload.index,
    hp: maxHp,
    maxHp,
    brillo: maxBrillo,
    maxBrillo,
    tono: Math.max(1, Math.round(payload.atk)),
    resistencia: Math.max(1, Math.round(payload.def)),
    tempo: Math.max(1, Math.round(payload.spd)),
    armonia: payload.rarity === "Primordial" ? 18 : payload.rarity === "Mayor" ? 14 : 10,
    shield: 0,
    skills: payload.skills.length > 0 ? payload.skills : [makeBasicSkill(normalizeElement(payload.element))],
    activeStates: [],
    crescendoUsed: false,
    lastTargetId: null,
    consecutiveHitsOnTarget: 0,
  };
};

async function loadSkillsBySonusIds(db: Database, sonusIds: number[]) {
  if (!sonusIds.length) return new Map<number, any[]>();
  const placeholders = sonusIds.map(() => "?").join(",");
  const rows = await db.all(
    `SELECT id_sonus, fase, nombre, descripcion, efecto, sprite_path
     FROM sonus_skills
     WHERE id_sonus IN (${placeholders})
     ORDER BY id_sonus ASC, fase ASC`,
    sonusIds
  );

  const grouped = new Map<number, any[]>();
  for (const row of rows) {
    const key = Number(row.id_sonus);
    const bucket = grouped.get(key) ?? [];
    bucket.push(row);
    grouped.set(key, bucket);
  }
  return grouped;
}

export async function buildTowerBattlePayload(db: Database, playerId: string, simulationId: string): Promise<TowerBattlePayload | null> {
  const simulationRow = await db.get(
    `SELECT * FROM tower_simulations WHERE simulation_id = ? AND player_id = ?`,
    [simulationId, playerId]
  );
  if (!simulationRow) return null;

  const context = typeof simulationRow.context_json === "string"
    ? JSON.parse(simulationRow.context_json)
    : simulationRow.context_json ?? {};
  const encounter = typeof simulationRow.encounter_json === "string"
    ? JSON.parse(simulationRow.encounter_json)
    : simulationRow.encounter_json ?? {};

  const playerRows = await db.all(
    `SELECT p.id_instancia, p.id_sonus, p.nivel_actual, p.experiencia_actual, p.cantidad_copias, p.nivel_evolucion,
            p.hp_actual, p.atk_actual, p.def_actual, p.spd_actual,
            s.nombre, s.rareza, s.elemento, s.clase, s.instrumento
     FROM player_sonus p
     JOIN sonus_master s ON s.id_sonus = p.id_sonus
     WHERE p.user_id = ?
     ORDER BY p.nivel_actual DESC, p.nivel_evolucion DESC, s.nombre ASC
     LIMIT 3`,
    [playerId]
  );

  const enemyIds = new Set<number>();
  const guardianId = encounter?.guardian?.sonusMasterId ?? encounter?.guardian?.sonus_master_id;
  if (guardianId != null && Number.isFinite(Number(guardianId))) {
    enemyIds.add(Number(guardianId));
  }

  const enemyPoolIds = Array.isArray(encounter?.enemyPoolRecords) && encounter.enemyPoolRecords.length > 0
    ? encounter.enemyPoolRecords.map((row: any) => Number(row.id_sonus)).filter((id: number) => Number.isFinite(id) && id > 0)
    : Array.isArray(encounter?.enemyPool)
      ? encounter.enemyPool.map((id: any) => Number(id)).filter((id: number) => Number.isFinite(id) && id > 0)
      : [];
  for (const id of enemyPoolIds) enemyIds.add(id);

  if (!enemyIds.size) {
    enemyIds.add(1);
  }

  const enemyRows = await db.all(
    `SELECT id_sonus, nombre, rareza, elemento, clase, instrumento, base_hp, base_atk, base_def, base_spd
     FROM sonus_master
     WHERE id_sonus IN (${Array.from(enemyIds).map(() => "?").join(",")})
     ORDER BY id_sonus ASC`,
    Array.from(enemyIds)
  );

  const allySkillMap = await loadSkillsBySonusIds(db, playerRows.map((row: any) => Number(row.id_sonus)));
  const enemySkillMap = await loadSkillsBySonusIds(db, enemyRows.map((row: any) => Number(row.id_sonus)));

  const allies = playerRows.map((row: any, index: number) =>
    makeBattleEntity({
      idPrefix: "ally",
      index,
      sonusId: Number(row.id_sonus),
      name: String(row.nombre),
      element: String(row.elemento),
      instrument: row.instrumento,
      hp: Number(row.hp_actual ?? row.base_hp ?? 1),
      atk: Number(row.atk_actual ?? row.base_atk ?? 1),
      def: Number(row.def_actual ?? row.base_def ?? 1),
      spd: Number(row.spd_actual ?? row.base_spd ?? 1),
      rarity: String(row.rareza ?? ""),
      isEnemy: false,
      skills: mapSkills(allySkillMap.get(Number(row.id_sonus)) ?? [], row),
    })
  );

  const enemies = enemyRows.map((row: any, index: number) =>
    makeBattleEntity({
      idPrefix: "enemy",
      index,
      sonusId: Number(row.id_sonus),
      name: String(row.nombre),
      element: String(row.elemento),
      instrument: row.instrumento,
      hp: Number(row.base_hp ?? 1),
      atk: Number(row.base_atk ?? 1),
      def: Number(row.base_def ?? 1),
      spd: Number(row.base_spd ?? 1),
      rarity: String(row.rareza ?? ""),
      isEnemy: true,
      skills: mapSkills(enemySkillMap.get(Number(row.id_sonus)) ?? [], row),
    })
  );

  return {
    simulationId,
    playerId,
    context,
    encounter,
    allies,
    enemies,
    battleEngine: "features/battle",
  };
}
