import { Router } from "express";
import { getDb } from "../../src/db";
import { authenticateToken } from "../../src/middleware/auth.middleware";
import crypto from "crypto";

export const inventoryRouter = Router();

const RARITY_WEIGHTS: Record<string, number> = {
  "Débil": 100,
  "DÃ©bil": 100,
  "Debil": 100,
  "Clara": 70,
  "Común": 45,
  "ComÃºn": 45,
  "Comun": 45,
  "Brillante": 25,
  "Mayor": 15,
  "Primordial": 6,
  "Temporal": 3,
};

const normalizeRarityKey = (value: unknown) => {
  const raw = String(value ?? "").trim();
  return raw.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
};

const getRarityWeight = (rarity: unknown) => {
  const raw = String(rarity ?? "").trim();
  if (raw in RARITY_WEIGHTS) return RARITY_WEIGHTS[raw];

  const normalized = normalizeRarityKey(raw);
  switch (normalized) {
    case "debil":
      return 100;
    case "clara":
      return 70;
    case "comun":
      return 45;
    case "brillante":
      return 25;
    case "mayor":
      return 15;
    case "primordial":
      return 6;
    case "temporal":
      return 3;
    default:
      return 10;
  }
};

const pickWeightedSonus = <T extends { id_sonus: number; rareza: string }>(rows: T[]) => {
  if (!rows.length) return null;

  const totalWeight = rows.reduce((sum, row) => sum + getRarityWeight(row.rareza), 0);
  if (totalWeight <= 0) return rows[0];

  let roll = Math.random() * totalWeight;
  for (const row of rows) {
    roll -= getRarityWeight(row.rareza);
    if (roll < 0) return row;
  }

  return rows[rows.length - 1];
};

// GET /api/inventory
inventoryRouter.get("/", authenticateToken, async (req: any, res) => {
  try {
    const db = await getDb();
    
    // Join player_sonus and sonus_master
    const sonusList = await db.all(`
      SELECT 
        p.id_instancia as idInstancia,
        p.id_sonus as sonusId,
        p.nivel_actual as level,
        p.experiencia_actual as experience,
        p.cantidad_copias as copies,
        p.nivel_evolucion as evolution,
        p.hp_actual as hp,
        p.atk_actual as atk,
        p.def_actual as def,
        p.spd_actual as spd,
        s.nombre as name,
        s.rareza as rarity,
        s.elemento as element,
        s.clase as role,
        s.instrumento as instrument,
        s.id_constelacion as constellationId,
        s.nivel_maximo_base as maxLevel,
        c.nombre as constellationName,
        c.descripcion as constellationDesc
      FROM player_sonus p
      JOIN sonus_master s ON p.id_sonus = s.id_sonus
      LEFT JOIN constelaciones c ON s.id_constelacion = c.id_constelacion
      WHERE p.user_id = ?
      ORDER BY 
        CASE s.rareza 
          WHEN 'Primordial' THEN 1 
          WHEN 'Temporal' THEN 2 
          WHEN 'Mayor' THEN 3 
          WHEN 'Brillante' THEN 4 
          WHEN 'Común' THEN 5 
          ELSE 6 
        END,
        p.nivel_actual DESC,
        s.nombre ASC
    `, [req.user.id]);
    
    res.json({ sonus: sonusList });
  } catch (err: any) {
    console.error("Inventory error:", err);
    res.status(500).json({ error: "Failed to fetch inventory" });
  }
});

// POST /api/inventory/save-pulls
inventoryRouter.post("/save-pulls", authenticateToken, async (req: any, res) => {
  const { results } = req.body;
  if (!Array.isArray(results)) {
    return res.status(400).json({ error: "Invalid results format" });
  }

  try {
    const db = await getDb();
    const userId = req.user.id;

    await db.run("BEGIN TRANSACTION");

    for (const pull of results) {
      // Find matching sonus in master by id
      const master = await db.get("SELECT * FROM sonus_master WHERE id_sonus = ?", [pull.id]);
      if (!master) continue;

      // Check if user already has it
      const existing = await db.get("SELECT * FROM player_sonus WHERE user_id = ? AND id_sonus = ?", [userId, pull.id]);

      if (existing) {
        // Add a copy
        await db.run(`
          UPDATE player_sonus 
          SET cantidad_copias = cantidad_copias + 1 
          WHERE id_instancia = ?
        `, [existing.id_instancia]);
      } else {
        // Insert new
        const idInstancia = crypto.randomUUID();
        await db.run(`
          INSERT INTO player_sonus (
            id_instancia, user_id, id_sonus, nivel_actual, experiencia_actual, 
            cantidad_copias, nivel_evolucion, hp_actual, atk_actual, def_actual, spd_actual
          ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        `, [
          idInstancia, userId, master.id_sonus, 1, 0, 1, 0, 
          master.base_hp, master.base_atk, master.base_def, master.base_spd
        ]);
      }
    }

    await db.run("COMMIT");
    res.json({ success: true });
  } catch (err) {
    console.error("Error saving pulls:", err);
    // @ts-ignore
    await getDb().then(db => db.run("ROLLBACK")).catch(() => {});
    res.status(500).json({ error: "Failed to save pulls" });
  }
});

// POST /api/inventory/add-sonus
inventoryRouter.post("/add-sonus", authenticateToken, async (req: any, res) => {
  try {
    const { sonusId, candidateSonusIds } = req.body;
    const userId = req.user.id;

    if (!sonusId && !Array.isArray(candidateSonusIds)) {
      return res.status(400).json({ error: "Missing sonusId or candidateSonusIds" });
    }

    const db = await getDb();
    await db.run("BEGIN TRANSACTION");

    let master = null;
    let chosenSonusId = Number(sonusId) || 0;

    const candidateIds = Array.isArray(candidateSonusIds)
      ? Array.from(new Set(candidateSonusIds.map((id: unknown) => Number(id)).filter((id: number) => Number.isFinite(id) && id > 0)))
      : [];

    if (candidateIds.length > 0) {
      const placeholders = candidateIds.map(() => "?").join(",");
      const candidates = await db.all(
        `SELECT id_sonus, nombre, rareza, base_hp, base_atk, base_def, base_spd
         FROM sonus_master
         WHERE id_sonus IN (${placeholders})`,
        candidateIds
      );
      const chosen = pickWeightedSonus(candidates);
      if (chosen) {
        master = chosen;
        chosenSonusId = chosen.id_sonus;
      }
    }

    if (!master && chosenSonusId > 0) {
      master = await db.get("SELECT * FROM sonus_master WHERE id_sonus = ?", [chosenSonusId]);
    }

    if (!master) {
      await db.run("ROLLBACK");
      return res.status(404).json({ error: "Sonus not found in master database" });
    }

    const existing = await db.get("SELECT * FROM player_sonus WHERE user_id = ? AND id_sonus = ?", [userId, master.id_sonus]);
    const isNew = !existing;

    const idInstancia = crypto.randomUUID();
    await db.run(`
      INSERT INTO player_sonus (
        id_instancia, user_id, id_sonus, nivel_actual, experiencia_actual, 
        cantidad_copias, nivel_evolucion, hp_actual, atk_actual, def_actual, spd_actual
      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `, [
      idInstancia, userId, master.id_sonus, 1, 0, 1, 0,
      master.base_hp, master.base_atk, master.base_def, master.base_spd
    ]);

    await db.run("COMMIT");
    res.json({
      success: true,
      isNew,
      name: master.nombre,
      chosenSonusId: master.id_sonus,
      rarity: master.rareza,
    });
  } catch (err) {
    console.error("Error adding sonus drop:", err);
    // @ts-ignore
    await getDb().then(db => db.run("ROLLBACK")).catch(() => {});
    res.status(500).json({ error: "Failed to add sonus drop" });
  }
});
