import { Router } from "express";
import type { RowDataPacket, ResultSetHeader } from "mysql2";
import crypto from "crypto";
import db from "../db.js";
import { requireAuth, requireCampaignRole } from "../auth/middleware.js";

const router = Router();

// GET /api/campaigns — only campaigns current user is a member of
router.get("/", requireAuth, async (req, res) => {
  try {
    const userId = req.user!.userId;
    const [rows] = await db.execute<RowDataPacket[]>(
      `SELECT c.*, cm.role
       FROM campaigns c
       JOIN campaign_members cm ON cm.campaign_id = c.id
       WHERE cm.user_id = ?
       ORDER BY c.created_at DESC`,
      [userId]
    );
    res.json(rows);
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Internal server error" });
  }
});

// POST /api/campaigns — create campaign, creator becomes DM
router.post("/", requireAuth, async (req, res) => {
  const { name } = req.body;
  if (!name?.trim()) {
    res.status(400).json({ error: "Campaign name is required" });
    return;
  }
  const conn = await db.getConnection();
  try {
    await conn.beginTransaction();
    const [result] = await conn.execute<ResultSetHeader>(
      "INSERT INTO campaigns (name) VALUES (?)",
      [name.trim()]
    );
    const campaignId = result.insertId;
    await conn.execute(
      "INSERT INTO campaign_members (campaign_id, user_id, role) VALUES (?, ?, 'dm')",
      [campaignId, req.user!.userId]
    );
    await conn.commit();
    const [rows] = await db.execute<RowDataPacket[]>(
      "SELECT c.*, 'dm' as role FROM campaigns c WHERE c.id = ?",
      [campaignId]
    );
    res.status(201).json(rows[0]);
  } catch (err) {
    await conn.rollback();
    console.error(err);
    res.status(500).json({ error: "Internal server error" });
  } finally {
    conn.release();
  }
});

// GET /api/campaigns/:id
router.get("/:id", requireAuth, async (req, res) => {
  try {
    const [rows] = await db.execute<RowDataPacket[]>(
      "SELECT * FROM campaigns WHERE id = ?",
      [req.params.id]
    );
    if (rows.length === 0) {
      res.status(404).json({ error: "Campaign not found" });
      return;
    }
    res.json(rows[0]);
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Internal server error" });
  }
});

// DELETE /api/campaigns/:id — DM only
router.delete("/:id", requireAuth, requireCampaignRole("dm"), async (req, res) => {
  try {
    const [result] = await db.execute<ResultSetHeader>(
      "DELETE FROM campaigns WHERE id = ?",
      [req.params.id]
    );
    if (result.affectedRows === 0) {
      res.status(404).json({ error: "Campaign not found" });
      return;
    }
    res.status(204).end();
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Internal server error" });
  }
});

// GET /api/campaigns/:id/my-role — returns current user's role in this campaign
router.get("/:id/my-role", requireAuth, async (req, res) => {
  try {
    const [rows] = await db.execute<RowDataPacket[]>(
      "SELECT role FROM campaign_members WHERE campaign_id = ? AND user_id = ?",
      [req.params.id, req.user!.userId]
    );
    if (rows.length === 0) {
      res.status(403).json({ error: "Not a campaign member" });
      return;
    }
    res.json({ role: rows[0].role });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Internal server error" });
  }
});

// POST /api/campaigns/:id/invite — generate invite link (DM only)
router.post("/:id/invite", requireAuth, requireCampaignRole("dm"), async (req, res) => {
  try {
    const token = crypto.randomBytes(32).toString("hex");
    await db.execute(
      "INSERT INTO campaign_invites (campaign_id, token, created_by) VALUES (?, ?, ?)",
      [req.params.id, token, req.user!.userId]
    );
    const clientUrl = process.env.CLIENT_URL ?? "http://localhost:5173";
    res.json({ url: `${clientUrl}/join/${token}` });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Internal server error" });
  }
});

// POST /api/campaigns/join/:token — join campaign as player
router.post("/join/:token", requireAuth, async (req, res) => {
  try {
    const [inviteRows] = await db.execute<RowDataPacket[]>(
      `SELECT * FROM campaign_invites
       WHERE token = ? AND (expires_at IS NULL OR expires_at > NOW())`,
      [req.params.token]
    );
    if (inviteRows.length === 0) {
      res.status(404).json({ error: "Invite not found or expired" });
      return;
    }

    const campaignId = inviteRows[0].campaign_id as number;
    const userId = req.user!.userId;

    // Already a member? Return 200 silently
    const [existing] = await db.execute<RowDataPacket[]>(
      "SELECT role FROM campaign_members WHERE campaign_id = ? AND user_id = ?",
      [campaignId, userId]
    );
    if (existing.length > 0) {
      res.json({ campaignId, role: existing[0].role });
      return;
    }

    await db.execute(
      "INSERT INTO campaign_members (campaign_id, user_id, role) VALUES (?, ?, 'player')",
      [campaignId, userId]
    );
    res.json({ campaignId, role: "player" });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Internal server error" });
  }
});

export default router;
