import { Router } from "express";
import type { ParamsDictionary } from "express-serve-static-core";
import type { RowDataPacket, ExecuteValues } from "mysql2";
import db from "../db.js";
import { parseJson } from "../utils/parseJson.js";
import { requireAuth } from "../auth/middleware.js";

type CampaignParams = ParamsDictionary & { campaignId: string };

const router = Router({ mergeParams: true });

// GET /api/campaigns/:campaignId/rolls
router.get<CampaignParams>("/", async (req, res) => {
  try {
    const { campaignId } = req.params;
    const [rows] = await db.execute<RowDataPacket[]>(
      "SELECT * FROM roll_log WHERE campaign_id = ? ORDER BY created_at DESC LIMIT 50",
      [campaignId]
    );
    const parsed = rows.map((r) => ({
      ...r,
      rolls: (parseJson(r.rolls) as unknown) as unknown[],
      advantage: r.advantage === 1,
      disadvantage: r.disadvantage === 1,
      nat20: r.nat20 === 1,
    }));
    res.json(parsed);
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Internal server error" });
  }
});

// POST /api/campaigns/:campaignId/rolls/:rollId/undo
router.post("/:rollId/undo", requireAuth, async (req, res, next) => {
  try {
    const [rows] = await db.execute<RowDataPacket[]>(
      "SELECT * FROM roll_log WHERE id = ? AND campaign_id = ?",
      [req.params.rollId, req.params.campaignId]
    );
    if (rows.length === 0) return void res.status(404).json({ error: "Roll not found" });
    const entry = rows[0];
    if (entry.undone) return void res.status(400).json({ error: "Already undone" });
    if (!entry.metadata) return void res.status(400).json({ error: "Not undoable" });

    const meta = JSON.parse(entry.metadata as string);
    const changes: { type: string; [k: string]: unknown }[] = meta.changes ?? [];

    for (const change of [...changes].reverse()) {
      if (change.type === "hp_change") {
        await db.execute("UPDATE characters SET hp_current = ? WHERE id = ?", [change.previous, change.characterId] as ExecuteValues);
      } else if (change.type === "gear_removed") {
        const g = change.gear as Record<string, unknown>;
        await db.execute(
          "INSERT INTO character_gear (id, character_id, name, type, slot_count, properties, effects, game_item_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
          [g.id, g.character_id, g.name, g.type, g.slot_count, g.properties, g.effects, g.game_item_id] as ExecuteValues
        );
      } else if (change.type === "spell_exhausted") {
        await db.execute("UPDATE character_spells SET exhausted = 0 WHERE id = ?", [change.characterSpellId] as ExecuteValues);
      } else if (change.type === "condition_added") {
        await db.execute("DELETE FROM character_conditions WHERE id = ?", [change.conditionId] as ExecuteValues);
      }
    }

    await db.execute("UPDATE roll_log SET undone = 1 WHERE id = ?", [entry.id]);

    const io = req.app.get("io");
    io.to(req.params.campaignId).emit("roll:undone", { rollId: entry.id, campaignId: req.params.campaignId });

    res.json({ ok: true });
  } catch (err) { next(err); }
});

export default router;
