import express from "express";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
import crypto from "crypto";
import { JWT_SECRET, authMiddleware } from "../middleware/auth.js";
import {
  createUser,
  getUserByUsername,
  getUserByEmail,
  getUserById,
  createEmailToken,
  getEmailToken,
  deleteEmailToken,
  markEmailVerified,
  updatePassword,
} from "../db/usersDb.js";
import { sendConfirmationEmail, sendPasswordResetEmail } from "../email.js";

const router = express.Router();

function issueToken(user) {
  return jwt.sign(
    { userId: user.id, username: user.username, team: user.team, role: user.role },
    JWT_SECRET,
    { expiresIn: "7d" }
  );
}

function generateToken() {
  return crypto.randomBytes(32).toString("hex");
}

// POST /api/auth/register
router.post("/register", async (req, res) => {
  const { username, email, password, team } = req.body ?? {};
  if (!username || !email || !password || !team) {
    return res.status(400).json({ error: "missing_fields" });
  }
  if (team !== "blue" && team !== "red") {
    return res.status(400).json({ error: "invalid_team" });
  }
  if (typeof username !== "string" || username.length < 2 || username.length > 32) {
    return res.status(400).json({ error: "invalid_username" });
  }
  if (typeof password !== "string" || password.length < 6) {
    return res.status(400).json({ error: "password_too_short" });
  }
  try {
    const passwordHash = await bcrypt.hash(password, 12);
    const user = await createUser(username.trim(), email.trim().toLowerCase(), passwordHash, team);

    // Create and send confirmation token
    const token = generateToken();
    const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000);
    await createEmailToken(user.id, token, "confirm", expiresAt);
    sendConfirmationEmail(user.email, token).catch((e) =>
      console.error("[email] Failed to send confirmation email:", e.message)
    );

    return res.status(201).json({ pending_verification: true });
  } catch (e) {
    if (e.code === "23505") {
      if (e.constraint?.includes("email")) return res.status(409).json({ error: "email_taken" });
      return res.status(409).json({ error: "username_taken" });
    }
    console.error(e);
    return res.status(500).json({ error: "database_error" });
  }
});

// POST /api/auth/login
router.post("/login", async (req, res) => {
  const { username, password } = req.body ?? {};
  if (!username || !password) return res.status(400).json({ error: "missing_fields" });
  try {
    const user = await getUserByUsername(username.trim());
    if (!user) return res.status(401).json({ error: "invalid_credentials" });
    const valid = await bcrypt.compare(password, user.password_hash);
    if (!valid) return res.status(401).json({ error: "invalid_credentials" });
    if (!user.email_verified) return res.status(403).json({ error: "email_not_verified" });
    const token = issueToken(user);
    return res.json({
      token,
      user: { id: user.id, username: user.username, team: user.team, role: user.role },
    });
  } catch (e) {
    console.error(e);
    return res.status(500).json({ error: "database_error" });
  }
});

// GET /api/auth/me
router.get("/me", authMiddleware, async (req, res) => {
  try {
    const user = await getUserById(req.user.userId);
    if (!user) return res.status(404).json({ error: "user_not_found" });
    const token = issueToken(user);
    return res.json({ token, user });
  } catch (e) {
    console.error(e);
    return res.status(500).json({ error: "database_error" });
  }
});

// POST /api/auth/resend-confirmation
router.post("/resend-confirmation", async (req, res) => {
  const { email } = req.body ?? {};
  if (!email) return res.status(400).json({ error: "missing_fields" });
  try {
    const user = await getUserByEmail(email.trim().toLowerCase());
    // Always respond OK to avoid user enumeration
    if (!user || user.email_verified) return res.json({ ok: true });

    const token = generateToken();
    const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000);
    await createEmailToken(user.id, token, "confirm", expiresAt);
    sendConfirmationEmail(user.email, token).catch((e) =>
      console.error("[email] Failed to resend confirmation email:", e.message)
    );
    return res.json({ ok: true });
  } catch (e) {
    console.error(e);
    return res.status(500).json({ error: "database_error" });
  }
});

// GET /api/auth/confirm-email?token=TOKEN
router.get("/confirm-email", async (req, res) => {
  const { token } = req.query;
  if (!token) return res.redirect("/?confirm_error=invalid");
  try {
    const row = await getEmailToken(token, "confirm");
    if (!row) return res.redirect("/?confirm_error=invalid");
    if (new Date(row.expires_at) < new Date()) {
      await deleteEmailToken(token);
      return res.redirect("/?confirm_error=expired");
    }
    await markEmailVerified(row.user_id);
    await deleteEmailToken(token);
    return res.redirect("/?email_confirmed=1");
  } catch (e) {
    console.error(e);
    return res.redirect("/?confirm_error=server_error");
  }
});

// POST /api/auth/forgot-password
router.post("/forgot-password", async (req, res) => {
  const { email } = req.body ?? {};
  if (!email) return res.status(400).json({ error: "missing_fields" });
  try {
    const user = await getUserByEmail(email.trim().toLowerCase());
    // Always respond OK to avoid user enumeration
    if (!user) return res.json({ ok: true });

    const token = generateToken();
    const expiresAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour
    await createEmailToken(user.id, token, "reset", expiresAt);
    sendPasswordResetEmail(user.email, token).catch((e) =>
      console.error("[email] Failed to send reset email:", e.message)
    );
    return res.json({ ok: true });
  } catch (e) {
    console.error(e);
    return res.status(500).json({ error: "database_error" });
  }
});

// POST /api/auth/reset-password
router.post("/reset-password", async (req, res) => {
  const { token, password } = req.body ?? {};
  if (!token || !password) return res.status(400).json({ error: "missing_fields" });
  if (typeof password !== "string" || password.length < 6) {
    return res.status(400).json({ error: "password_too_short" });
  }
  try {
    const row = await getEmailToken(token, "reset");
    if (!row) return res.status(400).json({ error: "invalid_token" });
    if (new Date(row.expires_at) < new Date()) {
      await deleteEmailToken(token);
      return res.status(400).json({ error: "token_expired" });
    }
    const passwordHash = await bcrypt.hash(password, 12);
    await updatePassword(row.user_id, passwordHash);
    await deleteEmailToken(token);
    return res.json({ ok: true });
  } catch (e) {
    console.error(e);
    return res.status(500).json({ error: "database_error" });
  }
});

export default router;
