import { Router } from "express";
import type { RowDataPacket, ResultSetHeader } from "mysql2";
import bcrypt from "bcrypt";
import db from "../db.js";
import { signToken, COOKIE_NAME } from "../auth/jwt.js";
import { requireAuth } from "../auth/middleware.js";

const router = Router();
const BCRYPT_ROUNDS = 12;
const COOKIE_OPTS = {
  httpOnly: true,
  sameSite: "lax" as const,
  secure: process.env.NODE_ENV === "production",
  maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days in ms
};

// POST /api/auth/register
router.post("/register", async (req, res) => {
  try {
    const { email, username, password } = req.body;

    if (!email?.trim() || !username?.trim() || !password) {
      res.status(400).json({ error: "email, username, and password are required" });
      return;
    }
    if (password.length < 8) {
      res.status(400).json({ error: "Password must be at least 8 characters" });
      return;
    }
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
      res.status(400).json({ error: "Invalid email format" });
      return;
    }

    const [existing] = await db.execute<RowDataPacket[]>(
      "SELECT id FROM users WHERE email = ?",
      [email.trim().toLowerCase()]
    );
    if (existing.length > 0) {
      res.status(409).json({ error: "Email already registered" });
      return;
    }

    const passwordHash = await bcrypt.hash(password, BCRYPT_ROUNDS);
    const [result] = await db.execute<ResultSetHeader>(
      "INSERT INTO users (email, username, password_hash) VALUES (?, ?, ?)",
      [email.trim().toLowerCase(), username.trim(), passwordHash]
    );

    const token = signToken({
      userId: result.insertId,
      email: email.trim().toLowerCase(),
      username: username.trim(),
    });
    res.cookie(COOKIE_NAME, token, COOKIE_OPTS);
    res.status(201).json({
      userId: result.insertId,
      email: email.trim().toLowerCase(),
      username: username.trim(),
    });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Internal server error" });
  }
});

// POST /api/auth/login
router.post("/login", async (req, res) => {
  try {
    const { email, password } = req.body;

    if (!email?.trim() || !password) {
      res.status(400).json({ error: "email and password are required" });
      return;
    }

    const [rows] = await db.execute<RowDataPacket[]>(
      "SELECT id, email, username, password_hash FROM users WHERE email = ?",
      [email.trim().toLowerCase()]
    );
    if (rows.length === 0) {
      res.status(401).json({ error: "Invalid email or password" });
      return;
    }

    const user = rows[0];
    const valid = await bcrypt.compare(password, user.password_hash as string);
    if (!valid) {
      res.status(401).json({ error: "Invalid email or password" });
      return;
    }

    const token = signToken({
      userId: user.id as number,
      email: user.email as string,
      username: user.username as string,
    });
    res.cookie(COOKIE_NAME, token, COOKIE_OPTS);
    res.json({ userId: user.id, email: user.email, username: user.username });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "Internal server error" });
  }
});

// POST /api/auth/logout
router.post("/logout", (_req, res) => {
  res.clearCookie(COOKIE_NAME);
  res.json({ ok: true });
});

// GET /api/auth/me
router.get("/me", requireAuth, (req, res) => {
  res.json(req.user);
});

export default router;
