import type { Pool } from 'pg';

/**
 * Tipos de correos informativos cuyo envío puede desactivarse por rol.
 * NO incluye correos transaccionales críticos (welcome, password-reset,
 * payment-*, invitaciones) — esos siempre se envían.
 *
 * Estado de implementación:
 * - documento_subido:      ✅ implementado (owner + supervisor del contribuyente)
 * - weekly_update:         ✅ implementado (job tenant-wide, owners)
 * - subscription_expiring: ✅ implementado (aviso a owner)
 * - recordatorio_fiscal:   ⏳ placeholder para futuras alertas
 * - alertas_nuevas:        ✅ implementado (supervisor + auxiliares + clientes)
 * - recordatorio_proximo:  ✅ implementado (auxiliar/supervisor/cliente/owner)
 */
export const EMAIL_TYPES = [
  'documento_subido',
  'weekly_update',
  'subscription_expiring',
  'recordatorio_fiscal',
  'alertas_nuevas',
  'recordatorio_proximo',
] as const;

export type EmailType = (typeof EMAIL_TYPES)[number];

/**
 * Roles que pueden recibir notificaciones informativas. Se excluyen roles
 * que hoy no son destinatarios de ninguna notificación (cfo, contador, visor).
 */
export const NOTIFICATION_ROLES = [
  'owner',
  'supervisor',
  'auxiliar',
  'cliente',
] as const;

export type NotificationRole = (typeof NOTIFICATION_ROLES)[number];

export type EmailPreferences = Record<EmailType, boolean>;

export type RoleEmailPreferences = Record<EmailType, Record<NotificationRole, boolean>>;

/**
 * Default legacy (por contribuyente). Se mantiene por compatibilidad con la
 * columna `contribuyentes.email_preferences`; la UI nueva ya no lo usa.
 */
function applyDefaults(raw: Partial<Record<string, unknown>>): EmailPreferences {
  const out = {} as EmailPreferences;
  for (const t of EMAIL_TYPES) {
    out[t] = raw[t] === false ? false : true;
  }
  return out;
}

function sanitizeUuid(id: string): string {
  return id.replace(/[^a-f0-9-]/gi, '');
}

// ═══════════════════════════════════════════════════════════════════════════
// Preferencias por contribuyente (legacy — conservado por compatibilidad)
// ═══════════════════════════════════════════════════════════════════════════

export async function getContribuyenteEmailPreferences(
  pool: Pool,
  contribuyenteId: string,
): Promise<EmailPreferences> {
  const safeId = sanitizeUuid(contribuyenteId);
  const { rows } = await pool.query<{ email_preferences: Record<string, unknown> | null }>(
    `SELECT email_preferences FROM contribuyentes WHERE entidad_id = $1`,
    [safeId],
  );
  const raw = rows[0]?.email_preferences ?? {};
  return applyDefaults(raw);
}

export async function setContribuyenteEmailPreferences(
  pool: Pool,
  contribuyenteId: string,
  partial: Partial<EmailPreferences>,
): Promise<EmailPreferences> {
  const safeId = sanitizeUuid(contribuyenteId);
  const merged: Record<string, boolean> = {};
  for (const t of EMAIL_TYPES) {
    if (t in partial) merged[t] = partial[t] === true;
  }

  await pool.query(
    `UPDATE contribuyentes
        SET email_preferences = COALESCE(email_preferences, '{}'::jsonb) || $2::jsonb
      WHERE entidad_id = $1`,
    [safeId, JSON.stringify(merged)],
  );

  return getContribuyenteEmailPreferences(pool, contribuyenteId);
}

export async function getEmailPreferencesPorContribuyente(
  pool: Pool,
): Promise<Array<{ contribuyenteId: string; rfc: string; nombre: string; preferences: EmailPreferences }>> {
  const { rows } = await pool.query<{
    entidad_id: string;
    rfc: string;
    nombre: string;
    email_preferences: Record<string, unknown> | null;
  }>(
    `SELECT c.entidad_id, c.rfc, e.nombre, c.email_preferences
       FROM contribuyentes c
       JOIN entidades_gestionadas e ON e.id = c.entidad_id
      WHERE e.active = true
      ORDER BY e.nombre`,
  );

  return rows.map(r => ({
    contribuyenteId: r.entidad_id,
    rfc: r.rfc,
    nombre: r.nombre,
    preferences: applyDefaults(r.email_preferences ?? {}),
  }));
}

// ═══════════════════════════════════════════════════════════════════════════
// Preferencias por rol (nuevo modelo)
// ═══════════════════════════════════════════════════════════════════════════

function applyRoleDefaults(raw: Array<{ email_type: string; role: string; enabled: boolean }>): RoleEmailPreferences {
  const out = {} as RoleEmailPreferences;
  for (const t of EMAIL_TYPES) {
    out[t] = {} as Record<NotificationRole, boolean>;
    for (const r of NOTIFICATION_ROLES) {
      const row = raw.find(x => x.email_type === t && x.role === r);
      out[t][r] = row ? row.enabled : true;
    }
  }
  return out;
}

/**
 * Lee las preferencias de notificación por rol. Si la tabla está vacía para
 * un (type, role), asume `true` para no romper el comportamiento previo.
 */
export async function getRoleEmailPreferences(pool: Pool): Promise<RoleEmailPreferences> {
  const { rows } = await pool.query<{ email_type: string; role: string; enabled: boolean }>(
    `SELECT email_type, role, enabled FROM notification_role_preferences`
  );
  return applyRoleDefaults(rows);
}

/**
 * Actualiza una celda (emailType, role). Ignora valores desconocidos.
 */
export async function setRoleEmailPreference(
  pool: Pool,
  emailType: EmailType,
  role: NotificationRole,
  enabled: boolean,
): Promise<RoleEmailPreferences> {
  await pool.query(
    `INSERT INTO notification_role_preferences (email_type, role, enabled)
     VALUES ($1, $2, $3)
     ON CONFLICT (email_type, role) DO UPDATE SET enabled = EXCLUDED.enabled, updated_at = NOW()`,
    [emailType, role, enabled],
  );
  return getRoleEmailPreferences(pool);
}

/**
 * Devuelve true si el rol tiene habilitado el tipo de notificación.
 * Fallback a true si no hay fila (comportamiento seguro).
 */
export async function isRoleEnabled(
  pool: Pool,
  emailType: EmailType,
  role: NotificationRole,
): Promise<boolean> {
  const { rows } = await pool.query<{ enabled: boolean }>(
    `SELECT enabled FROM notification_role_preferences WHERE email_type = $1 AND role = $2`,
    [emailType, role],
  );
  return rows[0]?.enabled ?? true;
}

interface RecipientWithRole {
  email: string;
  role: NotificationRole;
}

/**
 * Filtra una lista de destinatarios con rol según las preferencias guardadas.
 * Si no hay preferencias para un (type, role), se conserva el destinatario.
 */
export async function filterRecipientsByRole(
  pool: Pool,
  emailType: EmailType,
  recipients: RecipientWithRole[],
): Promise<string[]> {
  const prefs = await getRoleEmailPreferences(pool);
  const typePrefs = prefs[emailType];
  const filtered = recipients.filter(r => {
    if (!typePrefs) return true;
    return typePrefs[r.role] !== false;
  });
  return [...new Set(filtered.map(r => r.email))];
}

export type { RecipientWithRole };
