import type { RequestHandler } from "express";
import jwt from "jsonwebtoken";

import type { AuthUser, UserRole } from "@rental/shared";

import { env } from "../config/env.js";
import { ForbiddenError, UnauthorizedError } from "../utils/app-error.js";

type TokenPayload = {
  sub: string;
  name: string;
  email: string;
  role: UserRole;
  isActive: boolean;
};

export const signAuthToken = (user: AuthUser) =>
  jwt.sign(
    {
      name: user.name,
      email: user.email,
      role: user.role,
      isActive: user.isActive
    },
    env.JWT_SECRET,
    {
      subject: user.id,
      expiresIn: env.JWT_EXPIRES_IN as jwt.SignOptions["expiresIn"]
    }
  );

export const authenticate: RequestHandler = (request, _response, next) => {
  const header = request.headers.authorization;

  if (!header?.startsWith("Bearer ")) {
    return next(new UnauthorizedError());
  }

  const token = header.slice("Bearer ".length);

  try {
    const payload = jwt.verify(token, env.JWT_SECRET) as TokenPayload;
    request.authUser = {
      id: payload.sub,
      name: payload.name,
      email: payload.email,
      role: payload.role,
      isActive: payload.isActive
    };
    next();
  } catch (_error) {
    next(new UnauthorizedError("Invalid or expired token"));
  }
};

export const authorize =
  (...roles: UserRole[]): RequestHandler =>
  (request, _response, next) => {
    if (!request.authUser) {
      return next(new UnauthorizedError());
    }

    if (!roles.includes(request.authUser.role)) {
      return next(new ForbiddenError());
    }

    next();
  };
