import { Request, Response, NextFunction } from "express";
import jwt from "jsonwebtoken";
import { db, usersTable } from "@workspace/db";
import { eq } from "drizzle-orm";

const resolvedSecret = process.env.JWT_SECRET ?? process.env.SESSION_SECRET;
if (!resolvedSecret) {
  if (process.env.NODE_ENV === "production") {
    throw new Error(
      "JWT_SECRET (or SESSION_SECRET) environment variable must be set in production",
    );
  }
  console.warn("[auth] Neither JWT_SECRET nor SESSION_SECRET set — using development-only fallback. Never use this in production.");
}
export const JWT_SECRET = resolvedSecret ?? "dev-only-jwt-secret-do-not-use-in-production";

export interface AuthUser {
  id: number;
  email: string;
  name: string;
  role: string;
}

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

export async function requireAuth(req: Request, res: Response, next: NextFunction): Promise<void> {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    res.status(401).json({ error: "Unauthorized" });
    return;
  }
  const token = authHeader.slice(7);
  try {
    const payload = jwt.verify(token, JWT_SECRET) as { userId: number };
    const [user] = await db.select().from(usersTable).where(eq(usersTable.id, payload.userId));
    if (!user) {
      res.status(401).json({ error: "User not found" });
      return;
    }
    req.user = { id: user.id, email: user.email, name: user.name, role: user.role };
    next();
  } catch {
    res.status(401).json({ error: "Invalid token" });
  }
}

export async function requireAdmin(req: Request, res: Response, next: NextFunction): Promise<void> {
  await requireAuth(req, res, () => {
    if (req.user?.role !== "admin") {
      res.status(403).json({ error: "Forbidden" });
      return;
    }
    next();
  });
}

/**
 * Restrict a route to one or more roles. Runs requireAuth first, then enforces
 * that the authenticated user's role is in the allowed set (admins always pass).
 */
export function requireRole(...roles: string[]) {
  return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    await requireAuth(req, res, () => {
      const role = req.user?.role;
      if (!role || (role !== "admin" && !roles.includes(role))) {
        res.status(403).json({ error: "Forbidden" });
        return;
      }
      next();
    });
  };
}
