import type { NextFunction, Request, Response } from "express";
import type { Role } from "@/db/types";
import { User } from "@/db/models";
import { verifyAccessToken } from "@/lib/auth/jwt";
import {
  hasAdminPermission,
  hasRole,
  isStaffRole,
  type AdminPermission,
} from "@/lib/auth/rbac";
import { AppError } from "@/lib/errors";
import { isAuthMysql } from "@/db/activeDatabase";
import { findUserByIdMysql } from "@/db/mysql/users";

export type AuthUser = {
  id: string;
  email: string;
  fullName: string;
  role: Role;
  status: string;
  locale: string;
};

declare global {
  namespace Express {
    interface Request {
      user?: AuthUser;
    }
  }
}

export async function requireAuth(req: Request, _res: Response, next: NextFunction) {
  try {
    const header = req.headers.authorization;
    if (!header?.startsWith("Bearer ")) {
      throw new AppError(401, "Unauthorized");
    }
    const token = header.slice(7);
    const payload = verifyAccessToken(token);

    if (isAuthMysql()) {
      const user = await findUserByIdMysql(payload.sub);
      if (!user || user.deletedAt || user.status === "SUSPENDED") {
        throw new AppError(401, "Unauthorized");
      }
      req.user = {
        id: user.id,
        email: user.email,
        fullName: user.fullName,
        role: user.role as Role,
        status: user.status,
        locale: user.locale,
      };
      next();
      return;
    }

    const user = await User.findOne({ _id: payload.sub, deletedAt: null })
      .select("email fullName role status locale")
      .lean();
    if (!user || user.status === "SUSPENDED") {
      throw new AppError(401, "Unauthorized");
    }
    req.user = {
      id: user._id,
      email: user.email,
      fullName: user.fullName,
      role: user.role as Role,
      status: user.status,
      locale: user.locale,
    };
    next();
  } catch (error) {
    if (error instanceof AppError) return next(error);
    next(new AppError(401, "Invalid token"));
  }
}

export function requireRoles(...roles: Role[]) {
  return (req: Request, _res: Response, next: NextFunction) => {
    if (!req.user) return next(new AppError(401, "Unauthorized"));
    if (!hasRole(req.user.role, roles)) return next(new AppError(403, "Forbidden"));
    next();
  };
}

/** Blocks booking/payment until email OTP verification completes. */
export function requireActiveAccount() {
  return (req: Request, _res: Response, next: NextFunction) => {
    if (!req.user) return next(new AppError(401, "Unauthorized"));
    if (req.user.status !== "ACTIVE") {
      return next(
        new AppError(403, "Email verification required before booking", {
          code: "EMAIL_VERIFICATION_REQUIRED",
          status: req.user.status,
        }),
      );
    }
    next();
  };
}

/** Any staff role that can open the admin panel. */
export function requireStaff() {
  return (req: Request, _res: Response, next: NextFunction) => {
    if (!req.user) return next(new AppError(401, "Unauthorized"));
    if (!isStaffRole(req.user.role)) return next(new AppError(403, "Forbidden"));
    next();
  };
}

export function requirePermission(...permissions: AdminPermission[]) {
  return (req: Request, _res: Response, next: NextFunction) => {
    if (!req.user) return next(new AppError(401, "Unauthorized"));
    if (!isStaffRole(req.user.role)) return next(new AppError(403, "Forbidden"));
    const ok = permissions.every((p) => hasAdminPermission(req.user!.role, p));
    if (!ok) return next(new AppError(403, "Forbidden"));
    next();
  };
}
