import { and, eq, gt, inArray, isNull, or } from 'drizzle-orm';
import {
  permissionTemplatePermissions,
  permissionTemplates,
  userPermissionOverrides,
  userPermissionTemplateAssignments
} from '@/db/schema';
import { db } from '@/lib/db';
import { getDefaultPermissionsForRole, type MembershipRole, type OrganizationRole } from './roles';
import { isPermission, normalizePermissions, type Permission } from './permissions';

type ResolveEffectivePermissionsParams = {
  userId: string;
  organizationId: string;
  fallbackPermissions?: readonly string[] | null;
  fallbackRole?: MembershipRole | OrganizationRole | null;
};

export type EffectivePermissionResult = {
  permissions: Permission[];
  permissionTemplateIds: number[];
  templatePermissions: Permission[];
  allowOverrides: Permission[];
  denyOverrides: Permission[];
};

export async function resolveEffectivePermissions({
  userId,
  organizationId,
  fallbackPermissions,
  fallbackRole
}: ResolveEffectivePermissionsParams): Promise<EffectivePermissionResult> {
  const now = new Date();

  const assignmentRows = await db
    .select({
      templateId: userPermissionTemplateAssignments.templateId
    })
    .from(userPermissionTemplateAssignments)
    .innerJoin(
      permissionTemplates,
      eq(permissionTemplates.id, userPermissionTemplateAssignments.templateId)
    )
    .where(
      and(
        eq(userPermissionTemplateAssignments.userId, userId),
        eq(userPermissionTemplateAssignments.organizationId, organizationId),
        eq(userPermissionTemplateAssignments.isActive, true),
        eq(permissionTemplates.isActive, true),
        isNull(permissionTemplates.deletedAt)
      )
    );

  const permissionTemplateIds = [...new Set(assignmentRows.map((row) => row.templateId))];

  const templatePermissionRows = permissionTemplateIds.length
    ? await db
        .select({
          permission: permissionTemplatePermissions.permission
        })
        .from(permissionTemplatePermissions)
        .where(inArray(permissionTemplatePermissions.templateId, permissionTemplateIds))
    : [];

  const overrideRows = await db
    .select({
      permission: userPermissionOverrides.permission,
      effect: userPermissionOverrides.effect
    })
    .from(userPermissionOverrides)
    .where(
      and(
        eq(userPermissionOverrides.userId, userId),
        eq(userPermissionOverrides.organizationId, organizationId),
        eq(userPermissionOverrides.isActive, true),
        isNull(userPermissionOverrides.revokedAt),
        or(isNull(userPermissionOverrides.expiresAt), gt(userPermissionOverrides.expiresAt, now))
      )
    );

  const templatePermissions = normalizePermissions(
    templatePermissionRows.map((row) => row.permission)
  );

  const basePermissions =
    permissionTemplateIds.length > 0
      ? templatePermissions
      : normalizePermissions(fallbackPermissions).length > 0
        ? normalizePermissions(fallbackPermissions)
        : getDefaultPermissionsForRole(fallbackRole ?? 'member');

  const allowOverrides: Permission[] = [];
  const denyOverrides: Permission[] = [];

  for (const row of overrideRows) {
    if (!isPermission(row.permission)) {
      continue;
    }

    if (row.effect === 'allow') {
      allowOverrides.push(row.permission);
      continue;
    }

    if (row.effect === 'deny') {
      denyOverrides.push(row.permission);
    }
  }

  const effectivePermissions = [...new Set<Permission>([...basePermissions, ...allowOverrides])].filter(
    (permission) => !denyOverrides.includes(permission)
  );

  return {
    permissions: effectivePermissions,
    permissionTemplateIds,
    templatePermissions,
    allowOverrides,
    denyOverrides
  };
}
