import { and, eq, inArray, isNull } from 'drizzle-orm';
import { crmRoleProfiles, crmUserRoleAssignments, memberships } from '@/db/schema';
import { db } from '@/lib/db';
import type { NotificationRecipientTarget } from '../types';

type ResolveRecipientsInput = {
  organizationId: string;
  actorUserId?: string | null;
  recipient: NotificationRecipientTarget;
  payload: Record<string, unknown>;
};

function uniqueUserIds(values: Array<string | null | undefined>) {
  return [...new Set(values.filter((value): value is string => typeof value === 'string' && value.length > 0))];
}

function getPayloadUserIds(payload: Record<string, unknown>, key: string) {
  const value = payload[key];
  if (!Array.isArray(value)) {
    return [];
  }

  return uniqueUserIds(value.filter((item): item is string => typeof item === 'string'));
}

async function resolveApprovalCurrentStepApprovers(
  organizationId: string,
  roleCode: string
) {
  const assignmentRows = await db
    .select({ userId: crmUserRoleAssignments.userId })
    .from(crmUserRoleAssignments)
    .innerJoin(
      crmRoleProfiles,
      and(
        eq(crmRoleProfiles.id, crmUserRoleAssignments.roleProfileId),
        eq(crmRoleProfiles.organizationId, organizationId),
        eq(crmRoleProfiles.code, roleCode),
        eq(crmRoleProfiles.isActive, true),
        isNull(crmRoleProfiles.deletedAt)
      )
    )
    .where(
      and(
        eq(crmUserRoleAssignments.organizationId, organizationId),
        eq(crmUserRoleAssignments.isActive, true),
        isNull(crmUserRoleAssignments.deletedAt)
      )
    );

  const fallbackRows = await db
    .select({ userId: memberships.userId })
    .from(memberships)
    .where(
      and(eq(memberships.organizationId, organizationId), eq(memberships.businessRole, roleCode))
    );

  return uniqueUserIds([
    ...assignmentRows.map((row) => row.userId),
    ...fallbackRows.map((row) => row.userId)
  ]);
}

export async function resolveNotificationRecipients(input: ResolveRecipientsInput): Promise<string[]> {
  let recipients: string[] = [];

  switch (input.recipient.type) {
    case 'explicit_user':
      recipients = uniqueUserIds([...(input.recipient.userIds ?? []), ...getPayloadUserIds(input.payload, 'explicitUserIds')]);
      break;
    case 'approval_requester':
      recipients = uniqueUserIds([input.payload.requestedByUserId as string | undefined]);
      break;
    case 'approval_current_step_approvers': {
      const roleCode =
        (input.payload.currentStepRoleCode as string | undefined) ??
        (input.payload.recipientRoleCode as string | undefined) ??
        null;
      recipients = roleCode
        ? await resolveApprovalCurrentStepApprovers(input.organizationId, roleCode)
        : [];
      break;
    }
    case 'approval_previous_actor':
      recipients = uniqueUserIds([input.payload.previousActorUserId as string | undefined]);
      break;
    case 'entity_owner':
      recipients = uniqueUserIds([input.payload.entityOwnerUserId as string | undefined]);
      break;
    case 'sales_owner':
      recipients = uniqueUserIds([input.payload.salesOwnerUserId as string | undefined]);
      break;
    default:
      recipients = [];
  }

  if (input.recipient.excludeActor && input.actorUserId) {
    return recipients.filter((userId) => userId !== input.actorUserId);
  }

  return recipients;
}
