import { eq, inArray } from 'drizzle-orm';
import { memberships, organizations, users } from '@/db/schema';
import {
  type BusinessRole,
  getDefaultBusinessRole,
  getDefaultPermissions,
  isBusinessRole,
  isMembershipRole,
  isSystemRole
} from '@/lib/auth/rbac';
import { AuthError, requireSession } from '@/lib/auth/session';
import { db } from '@/lib/db';

export type UserMembershipInput = {
  organizationId: string;
  membershipRole: 'admin' | 'user';
  businessRole: BusinessRole;
};

export type UserMutationInput = {
  name: string;
  email: string;
  password?: string;
  systemRole: 'super_admin' | 'user';
  memberships: UserMembershipInput[];
};

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

  if (session.user.systemRole === 'super_admin') {
    const allOrganizations = await db.select({ id: organizations.id }).from(organizations);

    return {
      session,
      isSuperAdmin: true,
      manageableOrganizationIds: allOrganizations.map((organization) => organization.id)
    };
  }

  const adminMemberships = await db.query.memberships.findMany({
    where: eq(memberships.userId, session.user.id)
  });

  const manageableOrganizationIds = adminMemberships
    .filter((membership) => membership.role === 'admin')
    .map((membership) => membership.organizationId);

  if (!manageableOrganizationIds.length) {
    throw new AuthError('Forbidden', 403);
  }

  return {
    session,
    isSuperAdmin: false,
    manageableOrganizationIds
  };
}

export function parseUserMutationPayload(body: unknown): UserMutationInput {
  if (!body || typeof body !== 'object') {
    throw new Error('Invalid user payload');
  }

  const payload = body as Record<string, unknown>;
  const membershipsPayload = Array.isArray(payload.memberships) ? payload.memberships : [];
  const name = typeof payload.name === 'string' ? payload.name.trim() : '';
  const email = typeof payload.email === 'string' ? payload.email.trim().toLowerCase() : '';
  const password = typeof payload.password === 'string' ? payload.password.trim() : undefined;
  const systemRoleValue = typeof payload.systemRole === 'string' ? payload.systemRole : 'user';

  if (!name || !email || !isSystemRole(systemRoleValue)) {
    throw new Error('Invalid user payload');
  }

  const memberships = membershipsPayload
    .map((membership) => {
      if (!membership || typeof membership !== 'object') {
        return null;
      }

      const value = membership as Record<string, unknown>;
      const organizationId =
        typeof value.organizationId === 'string' ? value.organizationId.trim() : '';
      const membershipRole = typeof value.membershipRole === 'string' ? value.membershipRole : '';
      const businessRole =
        typeof value.businessRole === 'string'
          ? value.businessRole
          : getDefaultBusinessRole(
              membershipRole === 'admin' || membershipRole === 'user' ? membershipRole : 'user'
            );

      if (!organizationId || !isMembershipRole(membershipRole) || !isBusinessRole(businessRole)) {
        return null;
      }

      return {
        organizationId,
        membershipRole,
        businessRole
      };
    })
    .filter((membership): membership is UserMembershipInput => membership !== null);

  const uniqueMemberships = [
    ...new Map(memberships.map((membership) => [membership.organizationId, membership])).values()
  ];

  if (!uniqueMemberships.length) {
    throw new Error('Select at least one organization');
  }

  return {
    name,
    email,
    password,
    systemRole: systemRoleValue,
    memberships: uniqueMemberships
  };
}

export async function validateManagedOrganizations(
  targetMemberships: UserMembershipInput[],
  manageableOrganizationIds: string[],
  isSuperAdmin: boolean
) {
  const organizationIds = targetMemberships.map((membership) => membership.organizationId);

  const organizationRows = await db
    .select({
      id: organizations.id
    })
    .from(organizations)
    .where(inArray(organizations.id, organizationIds));

  if (organizationRows.length !== organizationIds.length) {
    throw new Error('One or more organizations do not exist');
  }

  if (
    !isSuperAdmin &&
    organizationIds.some((organizationId) => !manageableOrganizationIds.includes(organizationId))
  ) {
    throw new AuthError('Forbidden', 403);
  }
}

export async function getUserMembershipRows(userId: string) {
  return db.query.memberships.findMany({
    where: eq(memberships.userId, userId)
  });
}

export async function assertManageableTargetUser(
  userId: string,
  manageableOrganizationIds: string[],
  isSuperAdmin: boolean
) {
  const user = await db.query.users.findFirst({
    where: eq(users.id, userId)
  });

  if (!user) {
    throw new AuthError('User not found', 404);
  }

  if (user.systemRole === 'super_admin' && !isSuperAdmin) {
    throw new AuthError('Only super admins can edit super admin users', 403);
  }

  if (!isSuperAdmin) {
    const targetMemberships = await getUserMembershipRows(userId);

    if (
      !targetMemberships.length ||
      targetMemberships.some(
        (membership) => !manageableOrganizationIds.includes(membership.organizationId)
      )
    ) {
      throw new AuthError('Forbidden', 403);
    }
  }

  return user;
}

export function buildMembershipValues(userId: string, targetMemberships: UserMembershipInput[]) {
  return targetMemberships.map((membership) => ({
    id: crypto.randomUUID(),
    userId,
    organizationId: membership.organizationId,
    role: membership.membershipRole,
    businessRole: membership.businessRole,
    permissions: getDefaultPermissions(membership.membershipRole, membership.businessRole)
  }));
}
