import express from "express";
import jwt from "jsonwebtoken";
import { getConfig } from "../configLoader.js";
import { computeWorldSeedState } from "../worldSeed.js";
import { authMiddleware, JWT_SECRET } from "../middleware/auth.js";
import {
  ensureSeedEpoch,
  getGridCells,
  insertCell,
  getExistingCell,
  getTeamCooldown,
  getUserCooldown,
  upsertUserCooldown,
  getScores,
  getEconScores,
  addEconScore,
  getElementBonus,
  setElementBonus,
  getDbCreatedAt,
  getVictoryPoints,
  getActivePlayerCounts,
  getActivePlayerIds,
  getMilitaryDeductions,
  addMilitaryDeduction,
  recordCellAttack,
  getCellAttackCount,
  setTileOwner,
  getTeamActionsRow,
  resetTeamActions,
  decrementTeamActions,
  decrementTeamActionsBy,
  checkTeamVisibility,
  insertTeamVisibility,
  getTeamVisibleCells,
} from "../db/gameDb.js";
import {
  nextNoonUtc,
  getUserActionsRow,
  resetUserActions,
  decrementUserActions,
  getUsersByIds,
} from "../db/usersDb.js";
import { computeCell, rowToCellPayload } from "../helpers/cell.js";
import { computeTeamMilitaryPower } from "../helpers/economy.js";
import { broadcast, broadcastToTeam } from "../ws/hub.js";

const router = express.Router();

// GET /api/config
router.get("/config", async (req, res) => {
  try {
    const worldSeed = await ensureSeedEpoch();
    const cfg = getConfig();
    const rot = cfg.databaseWipeoutIntervalSeconds;
    const ws = computeWorldSeedState(rot);

    let actionsRemaining = null;
    let teamActionsRemaining = null;
    const team = typeof req.query.team === "string" ? req.query.team : undefined;
    if (team === "blue" || team === "red") {
      const authHeader = req.headers["authorization"];
      if (authHeader && authHeader.startsWith("Bearer ")) {
        try {
          const payload = jwt.verify(authHeader.slice(7), JWT_SECRET);
          const bonus = await getElementBonus(worldSeed);
          const teamBonus = bonus[team] ?? 0;
          const effectiveQuota = Math.floor(cfg.dailyActionQuota * (1 + teamBonus / 100));
          const now = new Date();
          const quotaRow = await getUserActionsRow(payload.userId);
          if (!quotaRow || new Date(quotaRow.quota_reset_at) <= now) {
            actionsRemaining = effectiveQuota;
          } else {
            actionsRemaining = quotaRow.actions_remaining;
          }
        } catch { /* invalid token — return null */ }
      }

      // Team-wide quota: compute current remaining without consuming
      const now = new Date();
      const teamRow = await getTeamActionsRow(team);
      if (!teamRow || new Date(teamRow.quota_reset_at) <= now) {
        // Expired or unset: compute what it would be when refreshed
        const rows = await getGridCells(worldSeed);
        const milPower = computeTeamMilitaryPower(team, rows, cfg.militaryPower ?? {});
        const milDeductions = await getMilitaryDeductions(worldSeed);
        const milNet = milPower - (milDeductions[team] ?? 0);
        const milBonus = Math.floor(Math.max(0, milNet) / 10000);
        teamActionsRemaining = cfg.teamActionQuota + milBonus;
      } else {
        teamActionsRemaining = teamRow.actions_remaining;
      }
    }

    res.json({
      dailyActionQuota: cfg.dailyActionQuota,
      databaseWipeoutIntervalSeconds: rot,
      configReloadIntervalSeconds: cfg.configReloadIntervalSeconds,
      worldSeed,
      seedPeriodEndsAtUtc: ws.seedPeriodEndsAtUtc,
      seedPeriodStartsAtUtc: ws.seedPeriodStartsAtUtc,
      actionsRemaining,
      teamActionsRemaining,
      resourceWorth: cfg.resourceWorth ?? { common: {}, rare: {} },
      elementWorth: cfg.elementWorth ?? {},
      militaryPower: cfg.militaryPower ?? {},
    });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "config_error" });
  }
});

// GET /api/grid/:seed  (auth-aware: returns only team-visible cells)
router.get("/grid/:seed", async (req, res) => {
  const seed = decodeURIComponent(req.params.seed || "");
  try {
    const worldSeed = await ensureSeedEpoch();
    if (seed !== worldSeed) {
      return res.status(410).json({
        error: "seed_expired",
        worldSeed,
        seedPeriodEndsAtUtc: computeWorldSeedState(
          getConfig().databaseWipeoutIntervalSeconds
        ).seedPeriodEndsAtUtc,
      });
    }
    let rows = [];
    const authHeader = req.headers["authorization"];
    if (authHeader && authHeader.startsWith("Bearer ")) {
      try {
        const payload = jwt.verify(authHeader.slice(7), JWT_SECRET);
        rows = await getTeamVisibleCells(worldSeed, payload.team);
      } catch { /* invalid token — return empty grid */ }
    }
    res.json({ seed, cells: rows });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// POST /api/cell/reveal  — reveals a cell for THIS team only (private visibility)
router.post("/cell/reveal", authMiddleware, async (req, res) => {
  const seed = String(req.body?.seed ?? "");
  const team = req.user.team;
  const userId = req.user.userId;
  const x = Number(req.body?.x);
  const y = Number(req.body?.y);

  if (!seed || !Number.isInteger(x) || !Number.isInteger(y) || x < 0 || x >= 100 || y < 0 || y >= 100) {
    return res.status(400).json({ error: "invalid_body" });
  }

  try {
    const worldSeed = await ensureSeedEpoch();
    if (seed !== worldSeed) {
      return res.status(410).json({ error: "seed_expired", worldSeed });
    }

    // Check if already revealed by this team (free re-view, no action cost)
    const alreadyVisible = await checkTeamVisibility(worldSeed, team, x, y);

    if (!alreadyVisible) {
      // First reveal: deduct one user action
      const cfg = getConfig();
      if (cfg.dailyActionQuota > 0) {
        const bonus = await getElementBonus(worldSeed);
        const teamBonus = bonus[team] ?? 0;
        const effectiveQuota = Math.floor(cfg.dailyActionQuota * (1 + teamBonus / 100));
        const now = new Date();
        const quotaRow = await getUserActionsRow(userId);
        if (!quotaRow || new Date(quotaRow.quota_reset_at) <= now) {
          await resetUserActions(userId, effectiveQuota - 1, nextNoonUtc().toISOString());
        } else {
          const updated = await decrementUserActions(userId);
          if (!updated) {
            return res.status(429).json({ error: "quota_exhausted", actionsRemaining: 0 });
          }
        }
      }
      // Mark as visible for this team
      await insertTeamVisibility(worldSeed, team, x, y);
    }

    // Ensure cell data is in grid_cells (discovered_by stays NULL = neutral)
    const cell = computeCell(seed, x, y);
    const planetJson = cell.planet ? JSON.stringify(cell.planet) : null;
    await insertCell(seed, x, y, cell.exploitable, cell.hasPlanet, planetJson);

    // Track activity for active-player count
    await upsertUserCooldown(worldSeed, userId, team);

    const existing = await getExistingCell(seed, x, y);
    if (!existing) return res.status(500).json({ error: "insert_race" });

    broadcastToTeam(team, "cell-updated", {
      worldSeed,
      cell: rowToCellPayload(existing),
    });

    return res.json(rowToCellPayload(existing));
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// POST /api/cell/capture  — spend team actions to capture a planet
router.post("/cell/capture", authMiddleware, async (req, res) => {
  const seed = String(req.body?.seed ?? "");
  const team = req.user.team;
  const x = Number(req.body?.x);
  const y = Number(req.body?.y);

  if (!seed || !Number.isInteger(x) || !Number.isInteger(y) || x < 0 || x >= 100 || y < 0 || y >= 100) {
    return res.status(400).json({ error: "invalid_body" });
  }

  try {
    const worldSeed = await ensureSeedEpoch();
    if (seed !== worldSeed) return res.status(410).json({ error: "seed_expired", worldSeed });

    const existing = await getExistingCell(worldSeed, x, y);
    if (!existing) return res.status(404).json({ error: "cell_not_found" });
    if (!existing.has_planet) return res.status(409).json({ error: "no_planet" });
    if (existing.discovered_by === team) return res.status(409).json({ error: "already_owned" });

    // Compute capture cost (1 action per 10 billion population, doubled if opponent-controlled)
    const planet = existing.planet_json;
    const billions = planet?.population?.billions ?? 0;
    const baseCost = Math.max(1, Math.ceil(billions / 10));
    const isOpponentControlled = existing.discovered_by !== null && existing.discovered_by !== team;
    const cost = isOpponentControlled ? baseCost * 2 : baseCost;

    // Consume team actions
    const cfg = getConfig();
    const now = new Date();
    const teamRow = await getTeamActionsRow(team);
    if (!teamRow || new Date(teamRow.quota_reset_at) <= now) {
      // Compute fresh quota
      const rows = await getGridCells(worldSeed);
      const milPower = computeTeamMilitaryPower(team, rows, cfg.militaryPower ?? {});
      const milDeductions = await getMilitaryDeductions(worldSeed);
      const milNet = milPower - (milDeductions[team] ?? 0);
      const milBonus = Math.floor(Math.max(0, milNet) / 10000);
      const totalActions = cfg.teamActionQuota + milBonus;
      if (totalActions < cost) {
        return res.status(429).json({ error: "team_quota_exhausted", cost, teamActionsRemaining: totalActions });
      }
      await resetTeamActions(team, totalActions - cost, nextNoonUtc().toISOString());
    } else {
      if (teamRow.actions_remaining < cost) {
        return res.status(429).json({ error: "team_quota_exhausted", cost, teamActionsRemaining: teamRow.actions_remaining });
      }
      const updated = await decrementTeamActionsBy(team, cost);
      if (!updated) {
        return res.status(429).json({ error: "team_quota_exhausted", cost, teamActionsRemaining: 0 });
      }
    }

    // Transfer ownership to capturing team
    await setTileOwner(worldSeed, x, y, team, req.user.username);

    const updatedCell = await getExistingCell(worldSeed, x, y);
    const updatedTeamRow = await getTeamActionsRow(team);
    const updatedCellPayload = rowToCellPayload(updatedCell);

    // Team that made the capture always gets the update.
    broadcastToTeam(team, "cell-updated", {
      worldSeed,
      cell: updatedCellPayload,
    });

    // Opponent receives the update only if that team had visibility on this cell.
    const opposingTeam = team === "blue" ? "red" : "blue";
    const opposingVisible = await checkTeamVisibility(worldSeed, opposingTeam, x, y);
    if (opposingVisible) {
      broadcastToTeam(opposingTeam, "cell-updated", {
        worldSeed,
        cell: updatedCellPayload,
      });
    }

    broadcastToTeam(team, "team-quota-updated", {
      team,
      actionsRemaining: updatedTeamRow?.actions_remaining ?? null,
    });

    res.json({
      success: true,
      cell: updatedCellPayload,
      teamActionsRemaining: updatedTeamRow?.actions_remaining ?? null,
    });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// GET /api/team-quota?team=blue|red
router.get("/team-quota", async (req, res) => {
  const team = typeof req.query.team === "string" ? req.query.team : "";
  if (team !== "blue" && team !== "red") {
    return res.status(400).json({ error: "invalid_team" });
  }
  try {
    const worldSeed = await ensureSeedEpoch();
    const cfg = getConfig();
    const now = new Date();
    const teamRow = await getTeamActionsRow(team);
    let actionsRemaining;
    if (!teamRow || new Date(teamRow.quota_reset_at) <= now) {
      const rows = await getGridCells(worldSeed);
      const milPower = computeTeamMilitaryPower(team, rows, cfg.militaryPower ?? {});
      const milDeductions = await getMilitaryDeductions(worldSeed);
      const milNet = milPower - (milDeductions[team] ?? 0);
      const milBonus = Math.floor(Math.max(0, milNet) / 10000);
      actionsRemaining = cfg.teamActionQuota + milBonus;
    } else {
      actionsRemaining = teamRow.actions_remaining;
    }
    res.json({ team, actionsRemaining });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// GET /api/econ-scores
router.get("/econ-scores", async (_req, res) => {
  try {
    const worldSeed = await ensureSeedEpoch();
    const scores = await getEconScores(worldSeed);
    res.json(scores);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// POST /api/econ-scores/tick  body: { seed, blue, red }
router.post("/econ-scores/tick", async (req, res) => {
  const seed  = String(req.body?.seed ?? "");
  const blue  = Number(req.body?.blue ?? 0);
  const red   = Number(req.body?.red  ?? 0);
  try {
    const worldSeed = await ensureSeedEpoch();
    if (seed !== worldSeed) return res.status(410).json({ error: "seed_expired", worldSeed });
    await addEconScore(worldSeed, "blue", blue);
    await addEconScore(worldSeed, "red",  red);
    const scores = await getEconScores(worldSeed);
    res.json(scores);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// GET /api/element-bonus
router.get("/element-bonus", async (_req, res) => {
  try {
    const worldSeed = await ensureSeedEpoch();
    const bonus = await getElementBonus(worldSeed);
    res.json(bonus);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// POST /api/element-bonus/tick  body: { seed, blue, red }
router.post("/element-bonus/tick", async (req, res) => {
  const seed = String(req.body?.seed ?? "");
  const blue = Number(req.body?.blue ?? 0);
  const red  = Number(req.body?.red  ?? 0);
  try {
    const worldSeed = await ensureSeedEpoch();
    if (seed !== worldSeed) return res.status(410).json({ error: "seed_expired", worldSeed });
    await setElementBonus(worldSeed, "blue", blue);
    await setElementBonus(worldSeed, "red",  red);
    const bonus = await getElementBonus(worldSeed);
    res.json(bonus);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// GET /api/db-info
router.get("/db-info", async (_req, res) => {
  try {
    const createdAt = await getDbCreatedAt();
    res.json({ createdAt });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// GET /api/victory-points
router.get("/victory-points", async (_req, res) => {
  try {
    const vp = await getVictoryPoints();
    res.json(vp);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// GET /api/active-players
router.get("/active-players", async (_req, res) => {
  try {
    const worldSeed = await ensureSeedEpoch();
    const counts = await getActivePlayerCounts(worldSeed);
    res.json(counts);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// GET /api/active-players/names
router.get("/active-players/names", async (_req, res) => {
  try {
    const worldSeed = await ensureSeedEpoch();
    const playerIds = await getActivePlayerIds(worldSeed);
    const allIds = [...playerIds.blue, ...playerIds.red];
    const users = await getUsersByIds(allIds);
    const result = { blue: [], red: [] };
    for (const user of users) {
      if (result[user.team]) result[user.team].push(user.username);
    }
    res.json(result);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// GET /api/scores
router.get("/scores", async (_req, res) => {
  try {
    const worldSeed = await ensureSeedEpoch();
    const rows = await getScores(worldSeed);
    const scores = { blue: 0, red: 0 };
    for (const row of rows) {
      if (row.discovered_by === "blue") scores.blue = Number(row.cnt);
      if (row.discovered_by === "red") scores.red = Number(row.cnt);
    }
    res.json(scores);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// GET /api/military-deductions
router.get("/military-deductions", async (_req, res) => {
  try {
    const worldSeed = await ensureSeedEpoch();
    const deductions = await getMilitaryDeductions(worldSeed);
    res.json(deductions);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// POST /api/military/attack  body: { seed, x, y }
// Requires auth. The attacker's team must have enough computed military power.
// Deducts exactly 1.0 billion (= 1000 M) and logs the attack on the cell.
router.post("/military/attack", authMiddleware, async (req, res) => {
  const seed = String(req.body?.seed ?? "");
  const x    = Number(req.body?.x);
  const y    = Number(req.body?.y);
  const attackingTeam = req.user.team;

  if (!seed || !Number.isInteger(x) || !Number.isInteger(y) || x < 0 || x >= 100 || y < 0 || y >= 100) {
    return res.status(400).json({ error: "invalid_body" });
  }

  try {
    const worldSeed = await ensureSeedEpoch();
    if (seed !== worldSeed) return res.status(410).json({ error: "seed_expired", worldSeed });

    // Target cell must exist, have a planet, and belong to the opposing team
    const existing = await getExistingCell(worldSeed, x, y);
    if (!existing) return res.status(404).json({ error: "cell_not_found" });
    if (!existing.discovered_by || existing.discovered_by === attackingTeam) {
      return res.status(409).json({ error: "cannot_attack_own_or_neutral_tile" });
    }

    // Deduct 1 billion (1.0 in "billions" unit) from the attacking team
    const COST_BILLIONS = 1.0;
    await addMilitaryDeduction(worldSeed, attackingTeam, COST_BILLIONS);

    // Transfer tile ownership to the attacking team
    await setTileOwner(worldSeed, x, y, attackingTeam, req.user.username);

    // Record the attack event
    await recordCellAttack(worldSeed, x, y, attackingTeam);

    const deductions  = await getMilitaryDeductions(worldSeed);
    const updatedCell = await getExistingCell(worldSeed, x, y);
    const updatedCellPayload = rowToCellPayload(updatedCell);

    broadcast("military-deductions-updated", {
      worldSeed,
      deductions,
    });

    broadcastToTeam(attackingTeam, "cell-updated", {
      worldSeed,
      cell: updatedCellPayload,
    });

    const opposingTeam = attackingTeam === "blue" ? "red" : "blue";
    const opposingVisible = await checkTeamVisibility(worldSeed, opposingTeam, x, y);
    if (opposingVisible) {
      broadcastToTeam(opposingTeam, "cell-updated", {
        worldSeed,
        cell: updatedCellPayload,
      });
    }

    res.json({
      success: true,
      cell: updatedCellPayload,
      deductions,
    });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

// GET /api/cell/attacks?x=&y=
router.get("/cell/attacks", async (req, res) => {
  const x = Number(req.query.x);
  const y = Number(req.query.y);
  if (!Number.isInteger(x) || !Number.isInteger(y) || x < 0 || x >= 100 || y < 0 || y >= 100) {
    return res.status(400).json({ error: "invalid_params" });
  }
  try {
    const worldSeed = await ensureSeedEpoch();
    const count = await getCellAttackCount(worldSeed, x, y);
    res.json({ x, y, attackCount: count });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: "database_error" });
  }
});

export default router;