import { and, eq, isNull } from 'drizzle-orm';
import { auth } from '@/auth';
import { memberships, organizations } from '@/db/schema';
import { hasAllPermissions, hasAnyPermission, hasPermission } from '@/lib/auth/authorization';
import type { Permission } from '@/lib/auth/permissions';
import {
  canAccessEmployeeArea,
  canManageUsers,
  getDefaultPermissionsForRole,
  isHRD,
  isIT,
  isSuperAdmin
} from '@/lib/auth/roles';
import { db } from '@/lib/db';

export class AuthError extends Error {
  status: number;

  constructor(message: string, status: number) {
    super(message);
    this.name = 'AuthError';
    this.status = status;
  }
}

export async function requireSession() {
  const session = await auth();

  if (!session?.user?.id) {
    throw new AuthError('Unauthorized', 401);
  }

  return session;
}

export async function getCurrentUser() {
  const session = await auth();
  return session?.user ?? null;
}

export async function requireAuth() {
  return requireSession();
}

export async function requireSuperAdmin() {
  const session = await requireSession();

  if (!isSuperAdmin(session.user.systemRole)) {
    throw new AuthError('Forbidden', 403);
  }

  return session;
}

export async function requireSuperAdminOrganizationAccess() {
  await requireSuperAdmin();
  return requireOrganizationAccess();
}

export async function requireOrganizationAccess(options?: {
  role?: string;
  permission?: Permission;
}) {
  const session = await requireSession();

  if (!session.user.activeOrganizationId) {
    throw new AuthError('Organization membership required', 403);
  }

  // Verify the active organization exists; if not, find a valid fallback
  let targetOrganizationId = session.user.activeOrganizationId;

  const organization = await db.query.organizations.findFirst({
    where: and(
      eq(organizations.id, targetOrganizationId),
      eq(organizations.isActive, true),
      isNull(organizations.deletedAt)
    )
  });

  if (!organization) {
    // Organization was deleted; try to find a valid fallback from user's memberships
    const [fallbackMembership] = await db
      .select({
        organizationId: memberships.organizationId
      })
      .from(memberships)
      .innerJoin(organizations, eq(organizations.id, memberships.organizationId))
      .where(
        and(
          eq(memberships.userId, session.user.id),
          eq(organizations.isActive, true),
          isNull(organizations.deletedAt)
        )
      )
      .limit(1);

    if (!fallbackMembership) {
      throw new AuthError('No valid organizations found', 403);
    }

    targetOrganizationId = fallbackMembership.organizationId;
  }

  let membership = await db.query.memberships.findFirst({
    where: and(
      eq(memberships.userId, session.user.id),
      eq(memberships.organizationId, targetOrganizationId)
    )
  });

  if (!membership && isSuperAdmin(session.user.systemRole)) {
    const organization = await db.query.organizations.findFirst({
      where: and(
        eq(organizations.id, targetOrganizationId),
        eq(organizations.isActive, true),
        isNull(organizations.deletedAt)
      )
    });

    if (!organization) {
      throw new AuthError('Organization not found', 404);
    }

    const [createdMembership] = await db
      .insert(memberships)
      .values({
        id: crypto.randomUUID(),
        userId: session.user.id,
        organizationId: targetOrganizationId,
        role: 'admin',
        permissions: getDefaultPermissionsForRole('admin')
      })
      .returning();

    membership = createdMembership;
  }

  if (!membership) {
    throw new AuthError('Forbidden', 403);
  }

  if (options?.role && membership.role !== options.role) {
    throw new AuthError('Forbidden', 403);
  }

  const effectivePermissions = session.user.effectivePermissions ?? session.user.permissions;

  if (options?.permission && !effectivePermissions.includes(options.permission)) {
    throw new AuthError('Forbidden', 403);
  }

  const org = await db.query.organizations.findFirst({
    where: and(
      eq(organizations.id, membership.organizationId),
      eq(organizations.isActive, true),
      isNull(organizations.deletedAt)
    )
  });

  if (!org) {
    throw new AuthError('Organization not found', 404);
  }

  return {
    session,
    membership,
    organization: org
  };
}

export async function requireHRD() {
  const access = await requireOrganizationAccess();

  if (
    !isHRD({
      businessRole: access.session.user.businessRole,
      systemRole: access.session.user.systemRole,
      membershipRole: access.membership.role,
      role: access.session.user.role
    })
  ) {
    throw new AuthError('Forbidden', 403);
  }

  return access;
}

export async function requireIT() {
  const access = await requireOrganizationAccess();

  if (
    !isIT({
      businessRole: access.session.user.businessRole,
      systemRole: access.session.user.systemRole,
      membershipRole: access.membership.role,
      role: access.session.user.role
    })
  ) {
    throw new AuthError('Forbidden', 403);
  }

  return access;
}

export async function requireUserManagementAccess() {
  const access = await requireOrganizationAccess();

  if (
    !canManageUsers({
      businessRole: access.session.user.businessRole,
      systemRole: access.session.user.systemRole,
      membershipRole: access.membership.role,
      role: access.session.user.role
    })
  ) {
    throw new AuthError('Forbidden', 403);
  }

  return access;
}

export async function requireEmployee() {
  const access = await requireOrganizationAccess();

  if (
    !canAccessEmployeeArea({
      businessRole: access.session.user.businessRole,
      systemRole: access.session.user.systemRole,
      membershipRole: access.membership.role,
      role: access.session.user.role
    })
  ) {
    throw new AuthError('Forbidden', 403);
  }

  return access;
}

export async function requirePermission(permission: Permission) {
  const access = await requireOrganizationAccess();

  if (
    !isSuperAdmin(access.session.user.systemRole) &&
    !hasPermission(access.session.user.effectivePermissions, permission)
  ) {
    throw new AuthError('Forbidden', 403);
  }

  return access;
}

export async function requireAnyPermission(permissions: Permission[]) {
  const access = await requireOrganizationAccess();

  if (
    !isSuperAdmin(access.session.user.systemRole) &&
    !hasAnyPermission(access.session.user.effectivePermissions, permissions)
  ) {
    throw new AuthError('Forbidden', 403);
  }

  return access;
}

export async function requireAllPermissions(permissions: Permission[]) {
  const access = await requireOrganizationAccess();

  if (
    !isSuperAdmin(access.session.user.systemRole) &&
    !hasAllPermissions(access.session.user.effectivePermissions, permissions)
  ) {
    throw new AuthError('Forbidden', 403);
  }

  return access;
}
