import express from "express";
import {
  getAccountByPublicKey,
  getZooByAccountId,
  createZoo,
  getMapParams,
  getAllZoos,
  countPlayerZoos,
  createBotZoo,
  updateLastSeen,
  updateZooGameState,
} from "../db.js";
import { verifySignature, buildSignMessage, hashBody } from "../auth.js";

const router = express.Router();
const TIMESTAMP_TOLERANCE_MS = 5 * 60 * 1000;

function isTimestampValid(timestamp) {
  const now = Date.now();
  const ts = new Date(timestamp).getTime();
  return !Number.isNaN(ts) && Math.abs(now - ts) <= TIMESTAMP_TOLERANCE_MS;
}

function getBodyForSignature(req) {
  return req.bodyRaw !== undefined && req.bodyRaw !== null ? req.bodyRaw : "";
}

function isSignatureValid(req, publicKey, signature, timestamp) {
  const bodyHash = hashBody(getBodyForSignature(req));
  const path = req.originalUrl || req.baseUrl + req.path || req.path;
  const message = buildSignMessage(req.method, path, timestamp, bodyHash);
  return verifySignature(publicKey, signature, message);
}

function requireSignature() {
  return (req, res, next) => {
    const publicKey = req.headers["x-public-key"];
    const signature = req.headers["x-signature"];
    const timestamp = req.headers["x-timestamp"];
    if (!publicKey || !signature || !timestamp) {
      res.status(401).json({ error: "Missing X-Public-Key, X-Signature, or X-Timestamp" });
      return;
    }
    if (!isTimestampValid(timestamp)) {
      res.status(401).json({ error: "Invalid or expired timestamp" });
      return;
    }
    if (!isSignatureValid(req, publicKey, signature, timestamp)) {
      res.status(401).json({ error: "Invalid signature" });
      return;
    }
    getAccountByPublicKey(publicKey).then((account) => {
      if (!account) {
        res.status(401).json({ error: "Unknown account" });
        return;
      }
      req.account = account;
      next();
    }).catch(next);
  };
}

/** @returns {Promise<{ zoos: Array<object>, mapWidth: number, mapHeight: number }>}
 */
async function getZoosForMap() {
  const params = await getMapParams();
  let zoos = await getAllZoos();
  await countPlayerZoos();
  const need = Math.max(0, params.minZoos - zoos.length);
  for (let i = 0; i < need; i++) {
    const x = 10 + Math.random() * 80;
    const y = 10 + Math.random() * 80;
    const weights = { Basic: 1 + Math.floor(Math.random() * 2), Ocean: Math.floor(Math.random() * 2), Mountain: Math.floor(Math.random() * 2) };
    await createBotZoo(x, y, weights);
  }
  if (need > 0) zoos = await getAllZoos();
  return {
    zoos,
    mapWidth: params.mapWidth,
    mapHeight: params.mapHeight,
  };
}

/** GET /api/zoos — list zoos for map (no auth). Ensures minZoos with bots. */
router.get("/", async (req, res, next) => {
  try {
    const { zoos, mapWidth, mapHeight } = await getZoosForMap();
    const worldZoos = zoos.map((z) => ({
      id: z.id,
      name: z.name,
      x: z.x,
      y: z.y,
      animalWeights: z.animal_weights,
      game_state: z.game_state ?? null,
    }));
    res.json({ worldZoos, mapWidth, mapHeight });
  } catch (e) {
    next(e);
  }
});

/** GET /api/zoos/me — my zoo + game_state (auth). */
router.get("/me", requireSignature(), (req, res, next) => {
  getZooByAccountId(req.account.id)
    .then((zoo) => {
      if (!zoo) {
        res.status(404).json({ error: "No zoo for this account" });
        return;
      }
      updateLastSeen(req.account.id);
      res.json({
        zooId: zoo.id,
        name: zoo.name,
        x: zoo.x,
        y: zoo.y,
        game_state: zoo.game_state,
      });
    })
    .catch(next);
});

/** PATCH /api/zoos/me — update game_state (auth). Body: { game_state }. */
router.patch("/me", requireSignature(), (req, res, next) => {
  getZooByAccountId(req.account.id)
    .then((zoo) => {
      if (!zoo) {
        res.status(404).json({ error: "No zoo for this account" });
        return;
      }
      const game_state = req.body?.game_state;
      if (game_state === null || game_state === undefined || typeof game_state !== "object") {
        res.status(400).json({ error: "game_state object required" });
        return;
      }
      return updateZooGameState(zoo.id, game_state).then(() => {
        res.json({ ok: true });
      });
    })
    .catch(next);
});

/** POST /api/zoos/me — create my zoo (auth). Body: { name?, game_state }. */
router.post("/me", requireSignature(), (req, res, next) => {
  getZooByAccountId(req.account.id)
    .then((existing) => {
      if (existing) {
        res.status(409).json({ error: "Zoo already exists" });
        return;
      }
      const name = req.body?.name?.trim() || req.account.pseudo;
      const game_state = req.body?.game_state;
      if (game_state === null || game_state === undefined || typeof game_state !== "object") {
        res.status(400).json({ error: "game_state object required" });
        return;
      }
      const x = 25 + Math.random() * 50;
      const y = 25 + Math.random() * 50;
      return createZoo({ accountId: req.account.id, name, x, y, gameState: game_state }).then(({ id }) => {
        res.status(201).json({ zooId: id, name, x, y });
      });
    })
    .catch(next);
});

export default router;
