// FILE: backend/src/routes/auth.js (ESM) — Bearer only, refresh via header/body, forgot/reset password
import { Router } from "express";
import jwt from "jsonwebtoken";
import sql from "../db/index.js";
import { cookieOpts } from "../utils/cookie.js";
import bcrypt from "bcryptjs";
import { requireAuth } from "../middleware/auth.js";
import crypto from "node:crypto";

const r = Router();

/* =========================
 * CONFIG & HELPERS
 * ========================= */
// ใช้ค่าเดียวกับ middleware authJwt()
const JWT_SECRET = process.env.JWT_SECRET || "dev-secret";
const REFRESH_SECRET = process.env.REFRESH_SECRET || "dev-refresh-secret";
const ACCESS_TTL = process.env.ACCESS_TTL || "30m";
const REFRESH_TTL = process.env.REFRESH_TTL || "30d";
// อายุของ reset token (นาที)
const RESET_TTL_MIN = Number(process.env.RESET_TTL_MIN || 30);

function signAccessToken(user) {
  return jwt.sign(
    { user_id: user.user_id, username: user.username },
    JWT_SECRET,
    { expiresIn: ACCESS_TTL, issuer: "dms-backend" }
  );
}

function signRefreshToken(user) {
  return jwt.sign(
    { user_id: user.user_id, username: user.username, t: "refresh" },
    REFRESH_SECRET,
    { expiresIn: REFRESH_TTL, issuer: "dms-backend" }
  );
}

function getBearer(req) {
  const h = req.headers.authorization || "";
  if (!h.startsWith("Bearer ")) return null;
  const token = h.slice(7).trim();
  return token || null;
}

async function findUserByUsername(username) {
  const [rows] = await sql.query(
    `SELECT user_id, username, email, first_name, last_name, password_hash
     FROM users WHERE username=? LIMIT 1`,
    [username]
  );
  return rows?.[0] || null;
}

async function findUserByEmail(email) {
  const [rows] = await sql.query(
    `SELECT user_id, username, email, first_name, last_name, password_hash
     FROM users WHERE email=? LIMIT 1`,
    [email]
  );
  return rows?.[0] || null;
}

/* =========================
 * POST /api/auth/login
 * - รับ username/password
 * - ตรวจ bcrypt แล้วออก token+refresh_token (JSON)
 * ========================= */
r.post("/login", async (req, res) => {
  const { username, password } = req.body || {};
  if (!username || !password) {
    return res.status(400).json({ error: "USERNAME_PASSWORD_REQUIRED" });
  }

  const user = await findUserByUsername(username);
  if (!user) return res.status(401).json({ error: "INVALID_CREDENTIALS" });

  const ok = await bcrypt.compare(password, user.password_hash || "");
  if (!ok) return res.status(401).json({ error: "INVALID_CREDENTIALS" });

  const token = signAccessToken(user);
  const refresh_token = signRefreshToken(user);

  // set httpOnly cookies (ยังคงส่ง token ใน body กลับเช่นเดิม)
  res.cookie(
    "access_token",
    token,
    cookieOpts(parseInt(process.env.ACCESS_TTL_MS || "900000", 10))
  );
  res.cookie(
    "refresh_token",
    refresh_token,
    cookieOpts(parseInt(process.env.REFRESH_TTL_MS || "604800000", 10))
  );

  return res.json({
    token,
    refresh_token,
    user: {
      user_id: user.user_id,
      username: user.username,
      email: user.email,
      first_name: user.first_name,
      last_name: user.last_name,
    },
  });
});
/* =========================
 * GET /api/auth/me (cookie or bearer)
 * ========================= */
r.get("/me", requireAuth, async (req, res) => {
  return res.json({
    ok: true,
    user: { user_id: req.user.user_id, username: req.user.username },
  });
});
+(
  /* =========================
   * POST /api/auth/refresh
   * - รองรับ refresh token จาก:
   *   1) Authorization: Bearer <refresh_token>
   *   2) req.body.refresh_token
   * - ออก token ใหม่ + refresh ใหม่ (rotation)
   * ========================= */
  r.post("/refresh", async (req, res) => {
    const fromHeader = getBearer(req);
    const fromBody = (req.body || {}).refresh_token;
    const refreshToken = fromHeader || fromBody;
    if (!refreshToken) {
      return res.status(400).json({ error: "REFRESH_TOKEN_REQUIRED" });
    }

    try {
      const payload = jwt.verify(refreshToken, REFRESH_SECRET, {
        issuer: "dms-backend",
      });
      if (payload.t !== "refresh") throw new Error("bad token type");

      const [[user]] = await sql.query(
        `SELECT user_id, username, email, first_name, last_name
       FROM users WHERE user_id=? LIMIT 1`,
        [payload.user_id]
      );
      if (!user) return res.status(401).json({ error: "USER_NOT_FOUND" });

      // rotation
      const token = signAccessToken(user);
      const new_refresh = signRefreshToken(user);

      // rotate cookies
      res.cookie(
        "access_token",
        token,
        cookieOpts(parseInt(process.env.ACCESS_TTL_MS || "900000", 10))
      );
      res.cookie(
        "refresh_token",
        new_refresh,
        cookieOpts(parseInt(process.env.REFRESH_TTL_MS || "604800000", 10))
      );

      return res.json({
        token,
        refresh_token: new_refresh,
        user: {
          user_id: user.user_id,
          username: user.username,
          email: user.email,
          first_name: user.first_name,
          last_name: user.last_name,
        },
      });
    } catch {
      return res.status(401).json({ error: "INVALID_REFRESH_TOKEN" });
    }
  })
);

/* =========================
 * POST /api/auth/forgot-password
 * - รับ username หรือ email อย่างใดอย่างหนึ่ง
 * - สร้าง reset token แบบสุ่ม, เก็บ hash ใน DB พร้อมหมดอายุ
 * - ส่งเสมอ {ok:true} เพื่อลด user enumeration
 * - การ “ส่งอีเมล/ลิงก์รีเซ็ต” ให้ทำนอกระบบนี้ (เช่น n8n)
 * ========================= */
r.post("/forgot-password", async (req, res) => {
  const { username, email } = req.body || {};
  // หา user จาก username หรือ email (ถ้ามีทั้งสอง จะให้ username มาก่อน)
  let user = null;
  if (username) user = await findUserByUsername(username);
  if (!user && email) user = await findUserByEmail(email);

  // สร้างโทเคน “เหมือนจริง” เสมอ (แต่ถ้าไม่เจอ user ก็ไม่บอก)
  if (user) {
    const raw = crypto.randomBytes(32).toString("hex"); // โทเคนดิบ (ส่งทางอีเมล)
    const hash = crypto.createHash("sha256").update(raw).digest("hex"); // เก็บใน DB
    const expires = new Date(Date.now() + RESET_TTL_MIN * 60 * 1000);

    // ทำ invalid เก่า ๆ ของ user นี้ (optional)
    await sql.query(
      `UPDATE password_resets SET used_at=NOW()
       WHERE user_id=? AND used_at IS NULL AND expires_at < NOW()`,
      [user.user_id]
    );

    // บันทึก token ใหม่
    await sql.query(
      `INSERT INTO password_resets (user_id, token_hash, expires_at)
       VALUES (?,?,?)`,
      [user.user_id, hash, expires]
    );

    // TODO: ส่ง “raw token” ไปช่องทางปลอดภัย (เช่น n8n ส่งอีเมล)
    // ตัวอย่างลิงก์ที่ frontend จะใช้:
    // https://<frontend-domain>/reset-password?token=<raw>
    // คุณสามารถต่อ webhook ไป n8n ได้ที่นี่ถ้าต้องการ
  }

  // ไม่บอกว่าเจอหรือไม่เจอ user
  return res.json({ ok: true });
});

/* =========================
 * POST /api/auth/reset-password
 * - รับ token (จากลิงก์ในอีเมล) + new_password
 * - ตรวจ token_hash กับ DB, หมดอายุหรือถูกใช้ไปแล้วหรือยัง
 * - เปลี่ยนรหัสผ่าน/ปิดใช้ token
 * ========================= */
r.post("/reset-password", async (req, res) => {
  const { token, new_password } = req.body || {};
  if (!token || !new_password) {
    return res.status(400).json({ error: "TOKEN_AND_NEW_PASSWORD_REQUIRED" });
  }

  const token_hash = crypto.createHash("sha256").update(token).digest("hex");

  const [[row]] = await sql.query(
    `SELECT id, user_id, expires_at, used_at
     FROM password_resets
     WHERE token_hash=? LIMIT 1`,
    [token_hash]
  );

  if (!row) return res.status(400).json({ error: "INVALID_TOKEN" });
  if (row.used_at) return res.status(400).json({ error: "TOKEN_ALREADY_USED" });
  if (new Date(row.expires_at).getTime() < Date.now()) {
    return res.status(400).json({ error: "TOKEN_EXPIRED" });
  }

  // เปลี่ยนรหัสผ่าน
  const salt = await bcrypt.genSalt(10);
  const hash = await bcrypt.hash(new_password, salt);
  await sql.query(`UPDATE users SET password_hash=? WHERE user_id=?`, [
    hash,
    row.user_id,
  ]);

  // ปิดใช้ token นี้
  await sql.query(`UPDATE password_resets SET used_at=NOW() WHERE id=?`, [
    row.id,
  ]);

  return res.json({ ok: true });
});

/* =========================
 * POST /api/auth/logout — stateless
 * - frontend ลบ token เอง
 * ========================= */
r.post("/logout", (_req, res) => {
  res.clearCookie("access_token", { path: "/" });
  res.clearCookie("refresh_token", { path: "/" });
  return res.json({ ok: true });
});

export default r;

// หมายเหตุ: คุณอาจเพิ่ม /register, /forgot-password, /reset-password ตามต้องการ
// แต่ในโปรเจกต์นี้จะให้แอดมินสร้างบัญชีผู้ใช้ผ่าน /api/users แทน
