import type { Pool } from 'pg';
import { prisma } from '../config/database.js';
import { generarEventosFiscales } from './calendario-fiscal.service.js';
import { isDespachoTenant } from '@horux/shared';

interface AlertaManualGenerada {
  tipo: string;
  titulo: string;
  mensaje: string;
  prioridad: 'alta' | 'media';
  fechaVencimiento: string;
}

// Mapeo de eventos del calendario a tipos de alerta (legacy Horux360)
const EVENTO_A_ALERTA: Record<string, { prefijo: string; prioridad: 'alta' | 'media' }> = {
  'Declaración mensual ISR': { prefijo: 'decl-isr', prioridad: 'alta' },
  'Declaración mensual IVA': { prefijo: 'decl-iva', prioridad: 'alta' },
  'Declaración mensual IEPS': { prefijo: 'decl-ieps', prioridad: 'media' },
  'Pago provisional ISR': { prefijo: 'pago-isr', prioridad: 'alta' },
  'Pago provisional IVA': { prefijo: 'pago-iva', prioridad: 'alta' },
  'Pago provisional IEPS': { prefijo: 'pago-ieps', prioridad: 'media' },
  'Declaración de sueldos y salarios': { prefijo: 'decl-sueldos', prioridad: 'media' },
  'DIOT': { prefijo: 'diot', prioridad: 'media' },
  'Contabilidad electrónica': { prefijo: 'contabilidad', prioridad: 'media' },
  'Declaración anual PM': { prefijo: 'decl-anual-pm', prioridad: 'alta' },
  'Declaración anual PF': { prefijo: 'decl-anual-pf', prioridad: 'alta' },
  'Informativa Sueldos y Salarios': { prefijo: 'inf-sueldos', prioridad: 'media' },
};

/**
 * For despachos: generate alerts from the contribuyente's actual obligations
 * (obligaciones_contribuyente) instead of the static fiscal calendar.
 * Only generates alerts for obligations that the contribuyente actually has.
 */
async function sincronizarDesdeObligacionesContribuyente(
  pool: Pool,
  contribuyenteId: string,
): Promise<{ creadas: number; existentes: number }> {
  const hoy = new Date();
  const currentPeriodo = hoy.toISOString().substring(0, 7); // "2026-04"

  // Get active obligations for this contribuyente
  const { rows: obligaciones } = await pool.query(`
    SELECT id, nombre, frecuencia, fecha_limite AS "fechaLimite", created_at AS "createdAt"
    FROM obligaciones_contribuyente
    WHERE contribuyente_id = $1 AND activa = true
  `, [contribuyenteId]);

  // Get existing completions
  const { rows: completions } = await pool.query(`
    SELECT op.obligacion_id, op.periodo
    FROM obligacion_periodos op
    JOIN obligaciones_contribuyente oc ON oc.id = op.obligacion_id
    WHERE oc.contribuyente_id = $1 AND op.completada = true
  `, [contribuyenteId]);
  const completionSet = new Set(completions.map(c => `${c.obligacion_id}:${c.periodo}`));

  let creadas = 0;
  let existentes = 0;

  for (const ob of obligaciones) {
    const obStartPeriodo = ob.createdAt
      ? new Date(ob.createdAt).toISOString().substring(0, 7)
      : '2000-01';

    // Check current and previous month
    for (let offset = 0; offset <= 1; offset++) {
      const d = new Date(hoy.getFullYear(), hoy.getMonth() - offset, 1);
      const periodo = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;

      if (periodo < obStartPeriodo) continue;
      if (!appliesToPeriod(ob.frecuencia, periodo)) continue;
      if (completionSet.has(`${ob.id}:${periodo}`)) continue;

      // Generate alert type unique per obligation+period
      const tipoUnico = `ob-${ob.id}-${periodo}`;

      const { rows: existing } = await pool.query(
        `SELECT id FROM alertas WHERE tipo = $1`,
        [tipoUnico],
      );

      if (existing.length > 0) {
        existentes++;
        continue;
      }

      // Determine deadline (day 17 of next month for mensual)
      const [y, m] = periodo.split('-').map(Number);
      const nextMonth = m === 12 ? 1 : m + 1;
      const nextYear = m === 12 ? y + 1 : y;
      const fechaVencimiento = `${nextYear}-${String(nextMonth).padStart(2, '0')}-17`;

      const deadlineDate = new Date(fechaVencimiento + 'T23:59:59');
      const isPastDue = deadlineDate < hoy;
      const prioridad = isPastDue ? 'alta' : 'media';
      const statusLabel = isPastDue ? 'Vencida' : 'Pendiente';

      await pool.query(`
        INSERT INTO alertas (tipo, titulo, mensaje, prioridad, fecha_vencimiento)
        VALUES ($1, $2, $3, $4, $5)
      `, [
        tipoUnico,
        `${ob.nombre} - ${statusLabel}`,
        `${ob.fechaLimite || 'Sin fecha límite especificada'}. Periodo: ${periodo}`,
        prioridad,
        fechaVencimiento,
      ]);
      creadas++;
    }
  }

  return { creadas, existentes };
}

function appliesToPeriod(frecuencia: string | null, periodo: string): boolean {
  const [, month] = periodo.split('-').map(Number);
  switch (frecuencia) {
    case 'mensual': return true;
    case 'bimestral': return month % 2 === 1;
    case 'trimestral': return [1, 4, 7, 10].includes(month);
    case 'anual': return month === 3 || month === 4;
    case 'eventual': return false;
    default: return true;
  }
}

/**
 * Genera alertas manuales para eventos fiscales vencidos que no han sido resueltos.
 * Para despachos: usa obligaciones per-contribuyente.
 * Para Horux360: usa el calendario fiscal estático (legacy).
 */
export async function sincronizarAlertasManuales(
  pool: Pool,
  tenantId: string,
  contribuyenteId?: string | null,
): Promise<{ creadas: number; existentes: number }> {
  // Check if this is a despacho tenant
  const tenant = await prisma.tenant.findUnique({
    where: { id: tenantId },
    select: { rfc: true, createdAt: true },
  });
  if (!tenant) return { creadas: 0, existentes: 0 };

  // Despacho: use per-contribuyente obligations
  if (isDespachoTenant(tenant.rfc)) {
    if (contribuyenteId) {
      return sincronizarDesdeObligacionesContribuyente(pool, contribuyenteId);
    }
    // "Todos los RFCs": don't generate new alerts — individual contribuyente alerts already exist
    return { creadas: 0, existentes: 0 };
  }

  // Legacy Horux360: use static fiscal calendar
  const hoy = new Date();
  const añoActual = hoy.getFullYear();
  const fechaCreacion = tenant.createdAt || hoy;

  const eventosActual = await generarEventosFiscales(tenantId, añoActual);
  const eventosAnterior = await generarEventosFiscales(tenantId, añoActual - 1);
  const todosEventos = [...eventosAnterior, ...eventosActual];

  const vencidos = todosEventos.filter(e => {
    const fecha = new Date(e.fechaLimite + 'T23:59:59');
    return fecha < hoy && fecha >= fechaCreacion;
  });

  let creadas = 0;
  let existentes = 0;

  for (const evento of vencidos) {
    const config = EVENTO_A_ALERTA[evento.titulo];
    if (!config) continue;

    const tipoUnico = `${config.prefijo}-${evento.fechaLimite}`;

    const { rows: existing } = await pool.query(
      `SELECT id, resuelta FROM alertas WHERE tipo = $1`,
      [tipoUnico]
    );

    if (existing.length > 0) {
      existentes++;
      continue;
    }

    const esPago = evento.titulo.startsWith('Pago');
    const accion = esPago ? 'Pago pendiente' : 'No presentada';

    await pool.query(`
      INSERT INTO alertas (tipo, titulo, mensaje, prioridad, fecha_vencimiento)
      VALUES ($1, $2, $3, $4, $5)
    `, [
      tipoUnico,
      `${evento.titulo} - ${accion}`,
      `${evento.descripcion}. Fecha limite: ${new Date(evento.fechaLimite + 'T00:00:00').toLocaleDateString('es-MX', { day: 'numeric', month: 'long', year: 'numeric' })}`,
      config.prioridad,
      evento.fechaLimite,
    ]);

    creadas++;
  }

  return { creadas, existentes };
}

/**
 * Obtiene alertas manuales pendientes (no resueltas).
 * Filters by contribuyente or by user's accessible contribuyentes (for clientes).
 */
export async function getAlertasManualesPendientes(
  pool: Pool,
  contribuyenteId?: string | null,
  userId?: string | null,
  role?: string | null,
): Promise<any[]> {
  let contribuyenteFilter = '';
  const params: unknown[] = [];

  if (contribuyenteId) {
    // Specific contribuyente selected
    params.push(contribuyenteId);
    contribuyenteFilter = `AND (
      tipo LIKE 'ob-%' AND SUBSTRING(tipo FROM 4 FOR 36) IN (
        SELECT id::text FROM obligaciones_contribuyente WHERE contribuyente_id = $${params.length}
      )
    )`;
  } else if (role === 'cliente' && userId) {
    // Client with "Todos los RFCs" — only their accessible contribuyentes
    params.push(userId);
    contribuyenteFilter = `AND (
      tipo LIKE 'ob-%' AND SUBSTRING(tipo FROM 4 FOR 36) IN (
        SELECT id::text FROM obligaciones_contribuyente WHERE contribuyente_id IN (
          SELECT entidad_id FROM cliente_accesos WHERE user_id = $${params.length}
        )
      )
    )`;
  } else if (role === 'auxiliar' && userId) {
    // Auxiliar: only their subcarteras' contribuyentes
    params.push(userId);
    contribuyenteFilter = `AND (
      tipo LIKE 'ob-%' AND SUBSTRING(tipo FROM 4 FOR 36) IN (
        SELECT id::text FROM obligaciones_contribuyente WHERE contribuyente_id IN (
          SELECT ce.entidad_id FROM cartera_entidades ce
          JOIN carteras c ON c.id = ce.cartera_id
          WHERE c.auxiliar_user_id = $${params.length}
          UNION
          SELECT ce.entidad_id FROM cartera_entidades ce
          JOIN cartera_auxiliares ca ON ca.cartera_id = ce.cartera_id
          WHERE ca.auxiliar_user_id = $${params.length}
        )
      )
    )`;
  } else if (role === 'supervisor' && userId) {
    // Supervisor: only their carteras' contribuyentes
    params.push(userId);
    contribuyenteFilter = `AND (
      tipo LIKE 'ob-%' AND SUBSTRING(tipo FROM 4 FOR 36) IN (
        SELECT id::text FROM obligaciones_contribuyente WHERE contribuyente_id IN (
          SELECT ce.entidad_id FROM cartera_entidades ce
          JOIN carteras c ON c.id = ce.cartera_id AND c.parent_id IS NULL
          WHERE c.supervisor_user_id = $${params.length}
        )
      )
    )`;
  }

  // Exclude alerts for inactive obligations
  const inactiveFilter = `AND NOT (
    tipo LIKE 'ob-%' AND SUBSTRING(tipo FROM 4 FOR 36) IN (
      SELECT id::text FROM obligaciones_contribuyente WHERE activa = false
    )
  )`;

  const { rows } = await pool.query(`
    SELECT id, tipo, titulo, mensaje, prioridad,
           fecha_vencimiento as "fechaVencimiento",
           leida, resuelta, created_at as "createdAt"
    FROM alertas
    WHERE resuelta = false
    ${inactiveFilter}
    ${contribuyenteFilter}
    ORDER BY
      CASE prioridad WHEN 'alta' THEN 1 WHEN 'media' THEN 2 ELSE 3 END,
      fecha_vencimiento ASC
  `, params);

  return rows;
}

/**
 * Marca una alerta como resuelta (presentada/pagada).
 */
export async function resolverAlerta(pool: Pool, id: string): Promise<void> {
  await pool.query(
    `UPDATE alertas SET resuelta = true, leida = true WHERE id = $1`,
    [id]
  );
}
