import { Router } from "express";
import { z } from "zod";
import { config } from "../../config.js";
import { clientIp } from "../../lib/crypto.js";
import { randomToken } from "../../lib/crypto.js";
import { requireAuth } from "../../middleware/auth.js";
import { loginLimiter } from "../../middleware/rateLimit.js";
import { validateBody } from "../../middleware/validate.js";
import { CSRF_COOKIE } from "../../middleware/csrf.js";
import { changePassword, login, logout } from "./service.js";
import { one, query } from "../../db/client.js";

const passwordPolicy = z
  .string()
  .min(10, "كلمة المرور يجب ألا تقل عن 10 أحرف")
  .max(128)
  .regex(/[a-zA-Z]/, "يجب أن تحتوي كلمة المرور على حرف لاتيني واحد على الأقل")
  .regex(/[0-9]/, "يجب أن تحتوي كلمة المرور على رقم واحد على الأقل");

export const authRouter = Router();

/** Issue a CSRF token (double-submit cookie). Must be called before any POST. */
authRouter.get("/csrf", (req, res) => {
  const token = randomToken(24);
  res.cookie(CSRF_COOKIE, token, {
    httpOnly: false,
    sameSite: "strict",
    secure: config.isProduction,
    path: "/",
    maxAge: 12 * 60 * 60 * 1000,
  });
  res.json({ csrfToken: token });
});

authRouter.post(
  "/login",
  loginLimiter,
  validateBody(
    z.object({
      username: z.string().min(1).max(64),
      password: z.string().min(1).max(128),
    }),
  ),
  async (req, res, next) => {
    try {
      const meta = { ip: clientIp(req), userAgent: req.headers["user-agent"] ?? null };
      const result = await login(req.body.username, req.body.password, meta);
      res.cookie(config.session.cookieName, result.token, {
        httpOnly: true,
        sameSite: "strict",
        secure: config.isProduction,
        path: "/",
        maxAge: config.session.ttlMinutes * 60_000,
      });
      res.json({ user: result.user });
    } catch (error) {
      next(error);
    }
  },
);

authRouter.post("/logout", requireAuth, async (req, res, next) => {
  try {
    const meta = { ip: clientIp(req), userAgent: req.headers["user-agent"] ?? null };
    await logout(req.sessionId as string, req.user!.id, meta);
    res.clearCookie(config.session.cookieName, { path: "/" });
    res.json({ ok: true });
  } catch (error) {
    next(error);
  }
});

authRouter.get("/me", requireAuth, async (req, res, next) => {
  try {
    const user = req.user!;
    const prefs = await one<{ locale: string | null; theme: string | null; member_id: number | null }>(
      `SELECT locale, theme, member_id FROM users WHERE id = $1`,
      [user.id],
    );
    res.json({
      user: {
        id: user.id,
        username: user.username,
        fullName: user.fullName,
        email: user.email,
        mustChangePassword: user.mustChangePassword,
        isDemo: user.isDemo,
        roles: user.roles.map((r) => ({ code: r.code, nameAr: r.nameAr, governorateIds: r.governorateIds })),
        permissions: [...user.permissions].sort(),
        preferences: { locale: prefs?.locale ?? null, theme: prefs?.theme ?? null },
        hasMemberFile: prefs?.member_id != null,
      },
    });
  } catch (error) {
    next(error);
  }
});

/** Presentation preferences only — kept separate from security-sensitive account settings. */
authRouter.put(
  "/preferences",
  requireAuth,
  validateBody(
    z.object({
      locale: z.enum(["ar", "en"]).optional(),
      theme: z.enum(["light", "dark"]).optional(),
    }),
  ),
  async (req, res, next) => {
    try {
      const { locale, theme } = req.body as { locale?: string; theme?: string };
      await query(
        `UPDATE users SET locale = COALESCE($2, locale), theme = COALESCE($3, theme) WHERE id = $1`,
        [req.user!.id, locale ?? null, theme ?? null],
      );
      const row = await one<{ locale: string | null; theme: string | null }>(
        `SELECT locale, theme FROM users WHERE id = $1`,
        [req.user!.id],
      );
      res.json({ preferences: { locale: row?.locale ?? null, theme: row?.theme ?? null } });
    } catch (error) {
      next(error);
    }
  },
);

authRouter.post(
  "/change-password",
  requireAuth,
  validateBody(
    z.object({
      currentPassword: z.string().min(1).max(128),
      newPassword: passwordPolicy,
    }),
  ),
  async (req, res, next) => {
    try {
      await changePassword(req, req.user!.id, req.body.currentPassword, req.body.newPassword);
      res.json({ ok: true });
    } catch (error) {
      next(error);
    }
  },
);
