import type { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import bcrypt from "bcryptjs";
import type { DepartmentKey, UserRole } from "@/lib/types";
import { prisma } from "@/lib/prisma";
import { resolveHighestUserRole } from "@/lib/role-resolution";
import { canUseElevatedAccess, parseDepartmentKey, parseDepartmentRole, parseUserRole, resolveViewContext } from "@/lib/elevated-access";

const DEFAULT_ROLE: UserRole = "employee";

type UserAccessContext = {
  role: UserRole;
  department: DepartmentKey | null;
  departmentRole: string | null;
  name: string | null;
  image: string | null;
  email: string | null;
};

type SessionUpdatePayload = {
  viewAsRole?: unknown;
  viewAsDepartment?: unknown;
  viewAsDepartmentRole?: unknown;
  clearViewAs?: unknown;
};

function toBoolean(value: unknown): boolean {
  return value === true || value === "true" || value === 1;
}

function toRecord(value: unknown): Record<string, unknown> | null {
  if (!value || typeof value !== "object") {
    return null;
  }

  return value as Record<string, unknown>;
}

async function getUserAccessContext(userId: string): Promise<UserAccessContext> {
  const user = await prisma.user.findUnique({
    where: { id: userId },
    select: {
      name: true,
      email: true,
      image: true,
      department: true,
      departmentRole: true,
      userRoles: {
        include: { role: true },
        orderBy: { assignedAt: "asc" },
      },
    },
  });

  if (!user) {
    return {
      role: DEFAULT_ROLE,
      department: null,
      departmentRole: null,
      name: null,
      image: null,
      email: null,
    };
  }

  const role = resolveHighestUserRole(user.userRoles.map((entry) => entry.role.key));
  const department = role === "owner" ? user.department : user.department ?? "marketing";

  return {
    role,
    department,
    departmentRole: user.departmentRole,
    name: user.name,
    image: user.image,
    email: user.email,
  };
}

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prisma),
  session: {
    strategy: "jwt",
  },
  pages: {
    signIn: "/login",
  },
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: "Email", type: "email" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        const email = credentials?.email?.toLowerCase().trim();
        const password = credentials?.password ?? "";

        if (!email || !password) {
          return null;
        }

        const user = await prisma.user.findUnique({
          where: { email },
        });

        if (!user || !user.passwordHash || user.status !== "active" || !user.emailVerified) {
          return null;
        }

        const isValidPassword = await bcrypt.compare(password, user.passwordHash);
        if (!isValidPassword) {
          return null;
        }

        const accessContext = await getUserAccessContext(user.id);

        return {
          id: user.id,
          email: user.email,
          name: user.name,
          role: accessContext.role,
          department: accessContext.department,
          departmentRole: accessContext.departmentRole,
        };
      },
    }),
  ],
  callbacks: {
    async jwt({ token, user, trigger, session }) {
      const userId = user?.id ?? token.sub;
      if (!userId) {
        return token;
      }

      const context = await getUserAccessContext(userId);
      const emailFromToken = typeof token.email === "string" ? token.email : null;
      const canElevate = canUseElevatedAccess(context.email ?? user?.email ?? emailFromToken);

      token.baseRole = context.role;
      token.baseDepartment = context.department;
      token.baseDepartmentRole = context.departmentRole;
      token.canElevate = canElevate;
      token.name = context.name ?? user?.name ?? token.name;
      token.email = context.email ?? user?.email ?? token.email;
      token.picture = context.image ?? user?.image ?? token.picture;

      if (user?.id) {
        token.viewAsRole = null;
        token.viewAsDepartment = null;
        token.viewAsDepartmentRole = null;
      }

      if (!canElevate) {
        token.viewAsRole = null;
        token.viewAsDepartment = null;
        token.viewAsDepartmentRole = null;
      } else if (trigger === "update") {
        const payload = toRecord(session) as SessionUpdatePayload | null;
        if (payload) {
          if (toBoolean(payload.clearViewAs)) {
            token.viewAsRole = null;
            token.viewAsDepartment = null;
            token.viewAsDepartmentRole = null;
          } else {
            const requestedRole = parseUserRole(payload.viewAsRole);
            if (requestedRole) {
              token.viewAsRole = requestedRole;
              token.viewAsDepartment = parseDepartmentKey(payload.viewAsDepartment);
              token.viewAsDepartmentRole = parseDepartmentRole(payload.viewAsDepartmentRole);
            }
          }
        }
      }

      const resolvedView = resolveViewContext({
        baseRole: context.role,
        baseDepartment: context.department,
        baseDepartmentRole: context.departmentRole,
        viewAsRole: canElevate ? parseUserRole(token.viewAsRole) : null,
        viewAsDepartment: canElevate ? parseDepartmentKey(token.viewAsDepartment) : null,
        viewAsDepartmentRole: canElevate ? parseDepartmentRole(token.viewAsDepartmentRole) : null,
      });

      token.role = resolvedView.role;
      token.department = resolvedView.department;
      token.departmentRole = resolvedView.departmentRole;
      token.isViewingAs = resolvedView.isViewingAs;
      return token;
    },
    async session({ session, token }) {
      if (session.user) {
        session.user.id = token.sub ?? "";
        session.user.role = (token.role as UserRole | undefined) ?? DEFAULT_ROLE;
        session.user.department = (token.department as DepartmentKey | null | undefined) ?? null;
        session.user.departmentRole = (token.departmentRole as string | null | undefined) ?? null;
        session.user.canElevate = Boolean(token.canElevate);
        session.user.baseRole = (token.baseRole as UserRole | undefined) ?? session.user.role;
        session.user.baseDepartment = (token.baseDepartment as DepartmentKey | null | undefined) ?? null;
        session.user.baseDepartmentRole = (token.baseDepartmentRole as string | null | undefined) ?? null;
        session.user.isViewingAs = Boolean(token.isViewingAs);
        session.user.viewAsRole = (token.viewAsRole as UserRole | null | undefined) ?? null;
        session.user.viewAsDepartment = (token.viewAsDepartment as DepartmentKey | null | undefined) ?? null;
        session.user.viewAsDepartmentRole = (token.viewAsDepartmentRole as string | null | undefined) ?? null;
        session.user.name = typeof token.name === "string" ? token.name : session.user.name;
        session.user.email = typeof token.email === "string" ? token.email : session.user.email;
        session.user.image = typeof token.picture === "string" ? token.picture : session.user.image;
      }

      return session;
    },
  },
  secret: process.env.NEXTAUTH_SECRET,
};
