import { cookies } from "next/headers";
import { SignJWT, jwtVerify } from "jose";
import bcrypt from "bcryptjs";
import { prisma } from "./prisma";
import type { Department, Operation } from "./format";

const COOKIE = "absher_session";

function secret() {
  return new TextEncoder().encode(process.env.AUTH_SECRET ?? "absher-local-dev-secret");
}

export type SessionUser = {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  roleId: string;
  roleName: string;
  permissions: Record<string, Operation[]>;
};

export async function hashPassword(password: string) {
  return bcrypt.hash(password, 10);
}

export async function verifyPassword(password: string, hash: string) {
  return bcrypt.compare(password, hash);
}

export async function createSession(user: SessionUser) {
  const token = await new SignJWT({
    id: user.id,
    firstName: user.firstName,
    lastName: user.lastName,
    email: user.email,
    roleId: user.roleId,
    roleName: user.roleName,
    permissions: JSON.stringify(user.permissions),
  })
    .setProtectedHeader({ alg: "HS256" })
    .setIssuedAt()
    .setExpirationTime("14d")
    .sign(secret());
  const jar = await cookies();
  jar.set(COOKIE, token, {
    httpOnly: true,
    sameSite: "lax",
    path: "/",
    maxAge: 60 * 60 * 24 * 14,
  });
}

export async function clearSession() {
  const jar = await cookies();
  jar.delete(COOKIE);
}

export async function getSession(): Promise<SessionUser | null> {
  const jar = await cookies();
  const token = jar.get(COOKIE)?.value;
  if (!token) return null;
  try {
    const { payload } = await jwtVerify(token, secret());
    return {
      id: String(payload.id),
      firstName: String(payload.firstName),
      lastName: String(payload.lastName),
      email: String(payload.email),
      roleId: String(payload.roleId),
      roleName: String(payload.roleName),
      permissions: JSON.parse(String(payload.permissions ?? "{}")),
    };
  } catch {
    return null;
  }
}

function toSession(user: {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  roleId: string;
  role: { name: string; permissions: string };
}): SessionUser {
  return {
    id: user.id,
    firstName: user.firstName,
    lastName: user.lastName,
    email: user.email,
    roleId: user.roleId,
    roleName: user.role.name,
    permissions: JSON.parse(user.role.permissions) as Record<string, Operation[]>,
  };
}

export async function requireUser() {
  const session = await getSession();
  if (!session) return null;
  const db = await prisma.user.findUnique({
    where: { id: session.id },
    include: { role: true },
  });
  if (!db || !db.active) return null;
  return toSession(db);
}

export function can(user: SessionUser, department: Department, operation: Operation) {
  if (user.roleName === "Super Admin") return true;
  return user.permissions[department]?.includes(operation) ?? false;
}

/** Permissions outside the department × operation matrix, stored under `special` in the role JSON. */
export const SPECIAL_PERMISSIONS = ["editValidatedInvoices"] as const;
export type SpecialPermission = (typeof SPECIAL_PERMISSIONS)[number];

export function canSpecial(user: SessionUser, permission: SpecialPermission) {
  if (user.roleName === "Super Admin") return true;
  const special = (user.permissions as Record<string, string[]>).special;
  return special?.includes(permission) ?? false;
}

export async function login(email: string, password: string) {
  const user = await prisma.user.findUnique({
    where: { email: email.trim().toLowerCase() },
    include: { role: true },
  });
  if (!user || !user.active) return { error: "Invalid email or password." };
  const ok = await verifyPassword(password, user.passwordHash);
  if (!ok) return { error: "Invalid email or password." };
  await createSession(toSession(user));
  return { ok: true };
}
