/**
 * Notification-recipient resolution for inquiry alerts.
 *
 * A recipient setting (e.g. `inquiry_notification_recipients`) can be either:
 *  - the LEGACY shape: a bare `string[]` of email addresses, or
 *  - the structured shape: `{ emails, userIds, roleIds, everyone }`.
 *
 * `parseRecipientConfig` normalizes both (legacy array -> explicit emails), so
 * no data migration is needed and existing configs keep working.
 * `resolveRecipientEmails` expands users / roles / "everyone-with-access" into
 * concrete email addresses, merged with any explicit emails and deduped.
 */

import { and, eq, inArray } from 'drizzle-orm';

import { db } from '@/lib/db';
import { user, userPortRoles } from '@/lib/db/schema/users';
import { getSetting } from '@/lib/services/settings.service';
import { findUsersWithInterestsPermission } from '@/lib/services/inquiry-notifications.service';

export interface RecipientConfig {
  /** Explicit email addresses. */
  emails: string[];
  /** CRM user ids whose account email should receive the alert. */
  userIds: string[];
  /** Role ids; every user holding the role on the port is included. */
  roleIds: string[];
  /** When true, everyone on the port with the inquiry-view permission. */
  everyone: boolean;
}

function strArray(value: unknown): string[] {
  return Array.isArray(value)
    ? value.filter((v): v is string => typeof v === 'string' && v.trim().length > 0)
    : [];
}

/** Normalize a stored recipient setting value into a RecipientConfig. */
export function parseRecipientConfig(value: unknown): RecipientConfig {
  // Legacy shape: a bare string[] of email addresses.
  if (Array.isArray(value)) {
    return { emails: strArray(value), userIds: [], roleIds: [], everyone: false };
  }
  if (value && typeof value === 'object') {
    const o = value as Record<string, unknown>;
    return {
      emails: strArray(o.emails),
      userIds: strArray(o.userIds),
      roleIds: strArray(o.roleIds),
      everyone: o.everyone === true,
    };
  }
  return { emails: [], userIds: [], roleIds: [], everyone: false };
}

/** Expand a RecipientConfig into a deduped list of email addresses. */
export async function resolveRecipientEmails(
  portId: string,
  config: RecipientConfig,
): Promise<string[]> {
  const userIdSet = new Set<string>(config.userIds);

  if (config.everyone) {
    for (const id of await findUsersWithInterestsPermission(portId)) userIdSet.add(id);
  }

  if (config.roleIds.length > 0) {
    const rows = await db
      .select({ userId: userPortRoles.userId })
      .from(userPortRoles)
      .where(and(eq(userPortRoles.portId, portId), inArray(userPortRoles.roleId, config.roleIds)));
    for (const r of rows) userIdSet.add(r.userId);
  }

  const collected: string[] = [...config.emails];
  if (userIdSet.size > 0) {
    const rows = await db
      .select({ email: user.email })
      .from(user)
      .where(inArray(user.id, Array.from(userIdSet)));
    for (const r of rows) if (r.email) collected.push(r.email);
  }

  // Case-insensitive dedupe, preserving the first-seen form.
  const seen = new Set<string>();
  const result: string[] = [];
  for (const raw of collected) {
    const email = raw.trim();
    const key = email.toLowerCase();
    if (email && !seen.has(key)) {
      seen.add(key);
      result.push(email);
    }
  }
  return result;
}

/**
 * Load + resolve a recipient setting to concrete email addresses. Falls back to
 * `fallbackKey` (a single-string setting, default `inquiry_contact_email`) when
 * the primary resolves to nothing. Pass an empty `fallbackKey` to disable the
 * fallback.
 */
export async function resolveNotificationRecipients(
  portId: string,
  primaryKey: string,
  fallbackKey = 'inquiry_contact_email',
): Promise<string[]> {
  const primary = await getSetting(primaryKey, portId);
  const resolved = await resolveRecipientEmails(portId, parseRecipientConfig(primary?.value));
  if (resolved.length > 0) return resolved;

  if (!fallbackKey) return [];
  const fallback = await getSetting(fallbackKey, portId);
  return typeof fallback?.value === 'string' && fallback.value.length > 0 ? [fallback.value] : [];
}
