import { and, eq, inArray, isNull } from 'drizzle-orm';
import {
  crmRoleProfiles,
  crmUserRoleAssignments,
  memberships,
  organizations,
  users
} from '@/db/schema';
import {
  createCrmUserRoleAssignment,
  deleteCrmUserRoleAssignment,
  updateCrmUserRoleAssignment
} from '@/features/foundation/crm-role-assignments/server/service';
import {
  getDefaultBusinessRole,
  getDefaultPermissions,
  isMembershipRole,
  isRoleAssignmentScopeMode,
  isSystemRole
} from '@/lib/auth/rbac';
import { resolveCrmMembershipAccess } from '@/lib/auth/crm-access';
import { AuthError, requireSession } from '@/lib/auth/session';
import { db } from '@/lib/db';
import type {
  User,
  UserCrmRoleAssignment,
  UserCrmAccessSummary
} from '@/features/users/api/types';

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

export type UserCrmRoleAssignmentInput = {
  id?: string;
  organizationId: string;
  roleProfileId: string;
  branchScopeMode: 'all' | 'selected' | 'none' | 'inherit';
  branchScopeIds: string[];
  productTypeScopeMode: 'all' | 'selected' | 'none' | 'inherit';
  productTypeScopeIds: string[];
  isPrimary: boolean;
  isActive: boolean;
};

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

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 crmRoleAssignmentsPayload = Array.isArray(payload.crmRoleAssignments)
    ? payload.crmRoleAssignments
    : [];
  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 : '';

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

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

  const crmRoleAssignments = crmRoleAssignmentsPayload
    .map((assignment) => {
      if (!assignment || typeof assignment !== 'object') {
        return null;
      }

      const value = assignment as Record<string, unknown>;
      const organizationId =
        typeof value.organizationId === 'string' ? value.organizationId.trim() : '';
      const roleProfileId =
        typeof value.roleProfileId === 'string' ? value.roleProfileId.trim() : '';
      const id = typeof value.id === 'string' ? value.id.trim() : undefined;
      const branchScopeMode = typeof value.branchScopeMode === 'string' ? value.branchScopeMode : '';
      const productTypeScopeMode =
        typeof value.productTypeScopeMode === 'string' ? value.productTypeScopeMode : '';
      const branchScopeIds = Array.isArray(value.branchScopeIds)
        ? value.branchScopeIds.filter((item): item is string => typeof item === 'string')
        : [];
      const productTypeScopeIds = Array.isArray(value.productTypeScopeIds)
        ? value.productTypeScopeIds.filter((item): item is string => typeof item === 'string')
        : [];
      const isPrimary = value.isPrimary === true;
      const isActive = value.isActive !== false;

      if (
        !organizationId ||
        !roleProfileId ||
        !isRoleAssignmentScopeMode(branchScopeMode) ||
        !isRoleAssignmentScopeMode(productTypeScopeMode)
      ) {
        return null;
      }

      return {
        ...(id ? { id } : {}),
        organizationId,
        roleProfileId,
        branchScopeMode,
        branchScopeIds,
        productTypeScopeMode,
        productTypeScopeIds,
        isPrimary,
        isActive
      } satisfies UserCrmRoleAssignmentInput;
    })
    .filter((assignment): assignment is UserCrmRoleAssignmentInput => assignment !== 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,
    crmRoleAssignments
  };
}

export async function validateManagedOrganizations(
  targetMemberships: UserMembershipInput[],
  crmRoleAssignments: UserCrmRoleAssignmentInput[],
  manageableOrganizationIds: string[],
  isSuperAdmin: boolean
) {
  const organizationIds = [
    ...new Set([
      ...targetMemberships.map((membership) => membership.organizationId),
      ...crmRoleAssignments.map((assignment) => assignment.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);
  }

  const membershipOrganizationIds = new Set(
    targetMemberships.map((membership) => membership.organizationId)
  );

  if (
    crmRoleAssignments.some((assignment) => !membershipOrganizationIds.has(assignment.organizationId))
  ) {
    throw new Error('CRM role assignments must belong to a selected organization membership');
  }
}

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;
}

function getLegacyBusinessRoleForMembership(input: {
  membership: UserMembershipInput;
  crmRoleAssignments: UserCrmRoleAssignmentInput[];
  roleProfilesById: Map<string, { code: string }>;
}) {
  const orgAssignments = input.crmRoleAssignments.filter(
    (assignment) => assignment.organizationId === input.membership.organizationId && assignment.isActive
  );
  const primaryAssignment =
    orgAssignments.find((assignment) => assignment.isPrimary) ?? orgAssignments[0] ?? null;

  if (primaryAssignment) {
    return (
      input.roleProfilesById.get(primaryAssignment.roleProfileId)?.code ??
      getDefaultBusinessRole(input.membership.membershipRole)
    );
  }

  return getDefaultBusinessRole(input.membership.membershipRole);
}

export async function buildMembershipValues(
  userId: string,
  targetMemberships: UserMembershipInput[],
  crmRoleAssignments: UserCrmRoleAssignmentInput[]
) {
  const roleProfileIds = [...new Set(crmRoleAssignments.map((assignment) => assignment.roleProfileId))];
  const roleProfiles = roleProfileIds.length
    ? await db
        .select({
          id: crmRoleProfiles.id,
          code: crmRoleProfiles.code
        })
        .from(crmRoleProfiles)
        .where(and(inArray(crmRoleProfiles.id, roleProfileIds), isNull(crmRoleProfiles.deletedAt)))
    : [];
  const roleProfilesById = new Map(roleProfiles.map((profile) => [profile.id, profile]));

  return targetMemberships.map((membership) => ({
    id: crypto.randomUUID(),
    userId,
    organizationId: membership.organizationId,
    role: membership.membershipRole,
    businessRole: getLegacyBusinessRoleForMembership({
      membership,
      crmRoleAssignments,
      roleProfilesById
    }),
    permissions: getDefaultPermissions(
      membership.membershipRole,
      getLegacyBusinessRoleForMembership({
        membership,
        crmRoleAssignments,
        roleProfilesById
      })
    )
  }));
}

export async function syncCrmRoleAssignmentsForUser(
  userId: string,
  actorUserId: string,
  membershipsForUser: UserMembershipInput[],
  crmRoleAssignments: UserCrmRoleAssignmentInput[]
) {
  const membershipOrganizationIds = membershipsForUser.map((membership) => membership.organizationId);
  const existingAssignments = await db
    .select()
    .from(crmUserRoleAssignments)
    .where(eq(crmUserRoleAssignments.userId, userId));
  const assignmentsForManagedMemberships = existingAssignments.filter((assignment) =>
    membershipOrganizationIds.includes(assignment.organizationId)
  );
  const payloadIds = new Set(crmRoleAssignments.map((assignment) => assignment.id).filter(Boolean));

  for (const assignment of assignmentsForManagedMemberships) {
    if (payloadIds.has(assignment.id)) {
      continue;
    }

    const stillReferenced = crmRoleAssignments.some(
      (item) =>
        item.organizationId === assignment.organizationId &&
        item.roleProfileId === assignment.roleProfileId &&
        !item.id
    );

    if (!stillReferenced) {
      await deleteCrmUserRoleAssignment(assignment.organizationId, actorUserId, assignment.id);
    }
  }

  for (const assignment of crmRoleAssignments) {
    if (assignment.id) {
      await updateCrmUserRoleAssignment(assignment.organizationId, actorUserId, assignment.id, {
        branchScopeMode: assignment.branchScopeMode,
        branchScopeIds: assignment.branchScopeIds,
        productTypeScopeMode: assignment.productTypeScopeMode,
        productTypeScopeIds: assignment.productTypeScopeIds,
        isPrimary: assignment.isPrimary,
        isActive: assignment.isActive
      });
      continue;
    }

    await createCrmUserRoleAssignment(assignment.organizationId, actorUserId, {
      userId,
      roleProfileId: assignment.roleProfileId,
      branchScopeMode: assignment.branchScopeMode,
      branchScopeIds: assignment.branchScopeIds,
      productTypeScopeMode: assignment.productTypeScopeMode,
      productTypeScopeIds: assignment.productTypeScopeIds,
      isPrimary: assignment.isPrimary,
      isActive: assignment.isActive
    });
  }
}

function summarizeScope(mode: UserCrmAccessSummary['branchScopeMode'], ids: string[]) {
  if (mode === 'all') {
    return 'All';
  }

  if (mode === 'none' || ids.length === 0) {
    return 'None';
  }

  return ids.length <= 2 ? ids.join(' + ') : `${ids.slice(0, 2).join(' + ')} +${ids.length - 2}`;
}

export async function buildUserResponseModels(params: {
  scopedOrganizationIds?: string[];
  scopedUserIds?: string[];
}) {
  const membershipRows = await db
    .select({
      membershipId: memberships.id,
      userId: memberships.userId,
      organizationId: memberships.organizationId,
      role: memberships.role,
      businessRole: memberships.businessRole,
      permissions: memberships.permissions,
      branchScopeIds: memberships.branchScopeIds,
      productTypeScopeIds: memberships.productTypeScopeIds,
      createdAt: memberships.createdAt,
      updatedAt: memberships.updatedAt,
      organizationName: organizations.name
    })
    .from(memberships)
    .innerJoin(organizations, eq(organizations.id, memberships.organizationId))
    .where(
      and(
        ...(params.scopedOrganizationIds?.length
          ? [inArray(memberships.organizationId, params.scopedOrganizationIds)]
          : []),
        ...(params.scopedUserIds?.length ? [inArray(memberships.userId, params.scopedUserIds)] : [])
      )
    );

  if (!membershipRows.length) {
    return [];
  }

  const userIds = [...new Set(membershipRows.map((row) => row.userId))];
  const organizationIds = [...new Set(membershipRows.map((row) => row.organizationId))];
  const [userRows, assignmentRows, roleProfileRows] = await Promise.all([
    db
      .select({
        id: users.id,
        name: users.name,
        email: users.email,
        systemRole: users.systemRole,
        activeOrganizationId: users.activeOrganizationId
      })
      .from(users)
      .where(inArray(users.id, userIds)),
    db
      .select()
      .from(crmUserRoleAssignments)
      .where(
        and(
          inArray(crmUserRoleAssignments.userId, userIds),
          inArray(crmUserRoleAssignments.organizationId, organizationIds),
          isNull(crmUserRoleAssignments.deletedAt)
        )
      ),
    db
      .select({
        id: crmRoleProfiles.id,
        code: crmRoleProfiles.code,
        name: crmRoleProfiles.name
      })
      .from(crmRoleProfiles)
      .where(
        and(
          inArray(crmRoleProfiles.organizationId, organizationIds),
          isNull(crmRoleProfiles.deletedAt)
        )
      )
  ]);
  const userMap = new Map(userRows.map((row) => [row.id, row]));
  const roleProfileMap = new Map(roleProfileRows.map((row) => [row.id, row]));
  const assignmentsByUserOrg = new Map<string, UserCrmRoleAssignment[]>();

  for (const assignment of assignmentRows) {
    const roleProfile = roleProfileMap.get(assignment.roleProfileId);
    if (!roleProfile) {
      continue;
    }

    const key = `${assignment.userId}:${assignment.organizationId}`;
    const current = assignmentsByUserOrg.get(key) ?? [];
    current.push({
      id: assignment.id,
      organizationId: assignment.organizationId,
      roleProfileId: assignment.roleProfileId,
      roleProfileCode: roleProfile.code,
      roleProfileName: roleProfile.name,
      branchScopeMode: isRoleAssignmentScopeMode(assignment.branchScopeMode)
        ? assignment.branchScopeMode
        : 'inherit',
      branchScopeIds: assignment.branchScopeIds ?? [],
      productTypeScopeMode: isRoleAssignmentScopeMode(assignment.productTypeScopeMode)
        ? assignment.productTypeScopeMode
        : 'inherit',
      productTypeScopeIds: assignment.productTypeScopeIds ?? [],
      isPrimary: assignment.isPrimary,
      isActive: assignment.isActive
    });
    assignmentsByUserOrg.set(key, current);
  }

  const usersMap = new Map<string, User>();

  for (const membershipRow of membershipRows) {
    const dbUser = userMap.get(membershipRow.userId);

    if (!dbUser) {
      continue;
    }

    const resolvedAccess = await resolveCrmMembershipAccess({
      systemRole: dbUser.systemRole === 'super_admin' ? 'super_admin' : 'user',
      organizationId: membershipRow.organizationId,
      membership: {
        id: membershipRow.membershipId,
        userId: membershipRow.userId,
        organizationId: membershipRow.organizationId,
        role: membershipRow.role,
        businessRole: membershipRow.businessRole,
        permissions: membershipRow.permissions,
        branchScopeIds: membershipRow.branchScopeIds,
        productTypeScopeIds: membershipRow.productTypeScopeIds,
        createdAt: membershipRow.createdAt,
        updatedAt: membershipRow.updatedAt
      }
    });
    const userKey = membershipRow.userId;
    const existing = usersMap.get(userKey);
    const assignments =
      assignmentsByUserOrg.get(`${membershipRow.userId}:${membershipRow.organizationId}`) ?? [];
    const accessSummary: UserCrmAccessSummary = {
      organizationId: membershipRow.organizationId,
      organizationName: membershipRow.organizationName,
      primaryCrmRole: resolvedAccess.businessRole,
      primaryCrmRoleLabel:
        assignments.find((assignment) => assignment.isPrimary)?.roleProfileName ??
        assignments[0]?.roleProfileName ??
        resolvedAccess.roleProfileName,
      roleCount: assignments.filter((assignment) => assignment.isActive).length,
      permissions: resolvedAccess.permissions,
      ownershipScope: resolvedAccess.ownershipScope,
      branchScopeMode: resolvedAccess.branchScopeMode,
      branchScopeIds: resolvedAccess.branchScopeIds,
      productScopeMode: resolvedAccess.productScopeMode,
      productTypeScopeIds: resolvedAccess.productTypeScopeIds,
      approvalAuthority: resolvedAccess.approvalAuthority,
      usedLegacyFallback: assignments.length === 0
    };

    if (existing) {
      existing.crmRoleAssignments ??= [];
      existing.crmAccessSummaries ??= [];
      existing.organizations.push({
        id: membershipRow.organizationId,
        name: membershipRow.organizationName,
        role: membershipRow.role === 'admin' ? 'admin' : 'user'
      });
      existing.memberships.push({
        organizationId: membershipRow.organizationId,
        organizationName: membershipRow.organizationName,
        membershipRole: membershipRow.role === 'admin' ? 'admin' : 'user',
        businessRole: membershipRow.businessRole,
        isLegacyBusinessRoleDeprecated: assignments.length > 0
      });
      existing.crmRoleAssignments.push(...assignments);
      existing.crmAccessSummaries.push(accessSummary);
      continue;
    }

    usersMap.set(userKey, {
      id: dbUser.id,
      name: dbUser.name,
      email: dbUser.email,
      systemRole: dbUser.systemRole === 'super_admin' ? 'super_admin' : 'user',
      activeMembershipRole:
        dbUser.activeOrganizationId === membershipRow.organizationId && membershipRow.role === 'admin'
          ? 'admin'
          : dbUser.activeOrganizationId === membershipRow.organizationId
            ? 'user'
            : null,
      activeOrganizationId: dbUser.activeOrganizationId,
      organizations: [
        {
          id: membershipRow.organizationId,
          name: membershipRow.organizationName,
          role: membershipRow.role === 'admin' ? 'admin' : 'user'
        }
      ],
      memberships: [
        {
          organizationId: membershipRow.organizationId,
          organizationName: membershipRow.organizationName,
          membershipRole: membershipRow.role === 'admin' ? 'admin' : 'user',
          businessRole: membershipRow.businessRole,
          isLegacyBusinessRoleDeprecated: assignments.length > 0
        }
      ],
      crmRoleAssignments: [...assignments],
      crmAccessSummaries: [accessSummary],
      primaryCrmRole: null,
      crmRoleCount: 0,
      branchScopeSummary: 'None',
      productScopeSummary: 'None',
      hasLegacyBusinessRoleFallback: assignments.length === 0
    });
  }

  return [...usersMap.values()].map((user) => {
    const crmAccessSummaries = user.crmAccessSummaries ?? [];
    const crmRoleAssignments = user.crmRoleAssignments ?? [];
    const primaryAccess =
      crmAccessSummaries.find(
        (summary) => summary.organizationId === user.activeOrganizationId
      ) ?? crmAccessSummaries[0];

    return {
      ...user,
      organizations: user.organizations.toSorted((a, b) => a.name.localeCompare(b.name)),
      memberships: user.memberships.toSorted((a, b) =>
        a.organizationName.localeCompare(b.organizationName)
      ),
      crmRoleAssignments: crmRoleAssignments.toSorted((a, b) => {
        if (a.isPrimary !== b.isPrimary) {
          return a.isPrimary ? -1 : 1;
        }

        return a.roleProfileName.localeCompare(b.roleProfileName);
      }),
      crmAccessSummaries: crmAccessSummaries.toSorted((a, b) =>
        a.organizationName.localeCompare(b.organizationName)
      ),
      primaryCrmRole: primaryAccess?.primaryCrmRoleLabel ?? primaryAccess?.primaryCrmRole ?? null,
      crmRoleCount: crmRoleAssignments.filter((assignment) => assignment.isActive).length,
      branchScopeSummary: primaryAccess
        ? summarizeScope(primaryAccess.branchScopeMode, primaryAccess.branchScopeIds)
        : 'None',
      productScopeSummary: primaryAccess
        ? summarizeScope(primaryAccess.productScopeMode, primaryAccess.productTypeScopeIds)
        : 'None',
      hasLegacyBusinessRoleFallback: crmAccessSummaries.some(
        (summary) => summary.usedLegacyFallback
      )
    } satisfies User;
  });
}
