import type { Pool } from 'pg';

const VIGENTE = `status NOT IN ('Cancelado', '0')`;

export interface ConciliacionCfdi {
  id: number;
  uuid: string;
  type: string;
  serie: string | null;
  folio: string | null;
  fechaEmision: string;
  rfcEmisor: string;
  nombreEmisor: string;
  rfcReceptor: string;
  nombreReceptor: string;
  regimenFiscalEmisor: string | null;
  regimenFiscalReceptor: string | null;
  total: number;
  totalMxn: number;
  subtotal: number;
  descuento: number;
  moneda: string;
  tipoCambio: number;
  tipoComprobante: string | null;
  metodoPago: string | null;
  formaPago: string | null;
  usoCfdi: string | null;
  status: string | null;
  fechaCertSat: string | null;
  fechaPagoP: string | null;
  ivaTraslado: number;
  ivaRetencion: number;
  isrRetencion: number;
  conciliado: string | null;
  idConciliacion: number | null;
  conciliacion: {
    id: number;
    fechaDePago: string;
    banco: string;
    terminacionCuenta: string;
  } | null;
}

export async function getCfdisConConciliacion(
  pool: Pool,
  filters: {
    tipo: string;
    fechaInicio?: string;
    fechaFin?: string;
    regimen?: string;
    estado?: string;
    contribuyenteId?: string;
  }
): Promise<ConciliacionCfdi[]> {
  const params: any[] = [];
  let idx = 1;

  let where = `WHERE c.type = $${idx++} AND c.${VIGENTE}`;
  params.push(filters.tipo);

  // Excluir PPD en recibidos
  if (filters.tipo === 'RECIBIDO') {
    where += ` AND (c.metodo_pago IS NULL OR c.metodo_pago != 'PPD')`;
  }

  // Excluir PPD en emitidos para todos los regimenes excepto 605 y 616
  // (metodo_pago IS NULL cubre tipo P y otros casos sin metodo de pago)
  if (filters.tipo === 'EMITIDO') {
    where += ` AND (c.metodo_pago IS NULL OR c.metodo_pago != 'PPD' OR c.regimen_fiscal_emisor IN ('605','616'))`;
  }

  if (filters.fechaInicio) {
    where += ` AND CASE WHEN c.tipo_comprobante = 'P' THEN c.fecha_pago_p - interval '1 hour' ELSE COALESCE(c.fecha_efectiva, c.fecha_emision - interval '1 hour') END >= $${idx++}::date`;
    params.push(filters.fechaInicio);
  }
  if (filters.fechaFin) {
    where += ` AND CASE WHEN c.tipo_comprobante = 'P' THEN c.fecha_pago_p - interval '1 hour' ELSE COALESCE(c.fecha_efectiva, c.fecha_emision - interval '1 hour') END <= ($${idx++}::date + interval '1 day')`;
    params.push(filters.fechaFin);
  }
  if (filters.regimen) {
    const regimenCol = filters.tipo === 'EMITIDO' ? 'regimen_fiscal_emisor' : 'regimen_fiscal_receptor';
    where += ` AND c.${regimenCol} = $${idx++}`;
    params.push(filters.regimen);
  }
  if (filters.estado === 'conciliado') {
    where += ` AND c.conciliado = 'true'`;
  } else if (filters.estado === 'pendiente') {
    where += ` AND (c.conciliado IS NULL OR c.conciliado != 'true')`;
  }

  if (filters.contribuyenteId) {
    const safeId = filters.contribuyenteId.replace(/[^a-f0-9-]/gi, '');
    where += ` AND c.contribuyente_id = '${safeId}'`;
  }

  const { rows } = await pool.query(`
    SELECT
      c.id, c.uuid, c.type,
      c.serie, c.folio,
      c.fecha_emision as "fechaEmision",
      c.rfc_emisor as "rfcEmisor", c.nombre_emisor as "nombreEmisor",
      c.rfc_receptor as "rfcReceptor", c.nombre_receptor as "nombreReceptor",
      c.regimen_fiscal_emisor as "regimenFiscalEmisor", c.regimen_fiscal_receptor as "regimenFiscalReceptor",
      c.total, c.total_mxn as "totalMxn",
      c.subtotal, c.descuento,
      c.moneda, c.tipo_cambio as "tipoCambio",
      c.tipo_comprobante as "tipoComprobante",
      c.monto_pago_mxn as "montoPagoMxn",
      c.metodo_pago as "metodoPago",
      c.forma_pago as "formaPago",
      c.uso_cfdi as "usoCfdi",
      c.status,
      c.fecha_cert_sat as "fechaCertSat",
      c.fecha_pago_p as "fechaPagoP",
      c.iva_traslado as "ivaTraslado",
      c.iva_retencion as "ivaRetencion",
      c.isr_retencion as "isrRetencion",
      c.conciliado,
      c.id_conciliacion as "idConciliacion",
      con.id as "conId",
      con.fecha_de_pago as "conFechaDePago",
      b.banco as "conBanco",
      b.terminacion_cuenta as "conTerminacionCuenta"
    FROM cfdis c
    LEFT JOIN conciliaciones con ON con.id_cfdi = c.id
    LEFT JOIN bancos b ON b.id = con.id_banco
    ${where}
    ORDER BY COALESCE(c.fecha_pago_p, c.fecha_emision) DESC
  `, params);

  return rows.map((r: any) => ({
    id: r.id,
    uuid: r.uuid,
    type: r.type,
    serie: r.serie,
    folio: r.folio,
    fechaEmision: r.fechaEmision,
    rfcEmisor: r.rfcEmisor,
    nombreEmisor: r.nombreEmisor,
    rfcReceptor: r.rfcReceptor,
    nombreReceptor: r.nombreReceptor,
    regimenFiscalEmisor: r.regimenFiscalEmisor,
    regimenFiscalReceptor: r.regimenFiscalReceptor,
    total: Number(r.total),
    totalMxn: Number(r.totalMxn),
    subtotal: Number(r.subtotal || 0),
    descuento: Number(r.descuento || 0),
    moneda: r.moneda || 'MXN',
    tipoCambio: Number(r.tipoCambio || 1),
    tipoComprobante: r.tipoComprobante,
    montoPagoMxn: Number(r.montoPagoMxn || 0),
    // P usa monto_pago_mxn, PPD conciliada no suma (evitar duplicar con su P), resto usa total_mxn
    montoMxn: r.tipoComprobante === 'P'
      ? Number(r.montoPagoMxn || 0)
      : (r.metodoPago === 'PPD' && r.conciliado === 'true') ? 0 : Number(r.totalMxn || 0),
    metodoPago: r.metodoPago,
    formaPago: r.formaPago,
    usoCfdi: r.usoCfdi,
    status: r.status,
    fechaCertSat: r.fechaCertSat,
    fechaPagoP: r.fechaPagoP,
    ivaTraslado: Number(r.ivaTraslado || 0),
    ivaRetencion: Number(r.ivaRetencion || 0),
    isrRetencion: Number(r.isrRetencion || 0),
    conciliado: r.conciliado,
    idConciliacion: r.idConciliacion,
    conciliacion: r.conId ? {
      id: r.conId,
      fechaDePago: r.conFechaDePago,
      banco: r.conBanco,
      terminacionCuenta: r.conTerminacionCuenta,
    } : null,
  }));
}

export async function conciliar(
  pool: Pool,
  data: { cfdiIds: number[]; fechaDePago: string; idBanco: number },
  tenantCreatedYear: number,
): Promise<number> {
  const fechaPago = new Date(data.fechaDePago + 'T12:00:00');
  const anio = String(fechaPago.getFullYear());
  const mes = String(fechaPago.getMonth() + 1).padStart(2, '0');

  if (fechaPago.getFullYear() < tenantCreatedYear) {
    throw new Error(`Solo se puede conciliar del año ${tenantCreatedYear} en adelante`);
  }

  const { rows: bancoRows } = await pool.query(`SELECT id FROM bancos WHERE id = $1`, [data.idBanco]);
  if (bancoRows.length === 0) throw new Error('Banco no encontrado');

  const { rows: cfdis } = await pool.query(`
    SELECT id, conciliado FROM cfdis
    WHERE id = ANY($1) AND ${VIGENTE}
  `, [data.cfdiIds]);

  if (cfdis.length !== data.cfdiIds.length) {
    throw new Error('Algunos CFDIs no existen o estan cancelados');
  }

  const yaConc = cfdis.filter((c: any) => c.conciliado === 'true');
  if (yaConc.length > 0) {
    throw new Error(`${yaConc.length} CFDIs ya estan conciliados`);
  }

  let count = 0;
  for (const cfdiId of data.cfdiIds) {
    const { rows: inserted } = await pool.query(`
      INSERT INTO conciliaciones (anio, mes, id_cfdi, fecha_de_pago, id_banco)
      VALUES ($1, $2, $3, $4, $5)
      RETURNING id
    `, [anio, mes, cfdiId, data.fechaDePago, data.idBanco]);

    await pool.query(`
      UPDATE cfdis SET conciliado = 'true', id_conciliacion = $1 WHERE id = $2
    `, [inserted[0].id, cfdiId]);

    count++;

    // Auto-conciliar PPD si esta factura tipo P lleva saldo pendiente a 0
    await autoConciliarPpd(pool, cfdiId, anio, mes, data.fechaDePago, data.idBanco);
  }

  return count;
}

/**
 * Cuando se concilia una factura tipo P con saldo_pendiente = 0,
 * auto-concilia la factura PPD original relacionada con los mismos datos.
 */
async function autoConciliarPpd(
  pool: Pool,
  cfdiId: number,
  anio: string,
  mes: string,
  fechaDePago: string,
  idBanco: number,
): Promise<void> {
  // Verificar si es tipo P con saldo pendiente 0
  const { rows: pRows } = await pool.query(`
    SELECT tipo_comprobante, uuid_relacionado, saldo_pendiente
    FROM cfdis WHERE id = $1
  `, [cfdiId]);

  const pCfdi = pRows[0];
  if (!pCfdi || pCfdi.tipo_comprobante !== 'P') return;
  if (!pCfdi.uuid_relacionado) return;

  const saldoPendiente = Number(pCfdi.saldo_pendiente || 0);
  if (saldoPendiente !== 0) return;

  // Buscar la factura PPD original por UUID
  const { rows: ppdRows } = await pool.query(`
    SELECT id, conciliado FROM cfdis
    WHERE uuid = $1 AND metodo_pago = 'PPD' AND ${VIGENTE}
  `, [pCfdi.uuid_relacionado]);

  if (ppdRows.length === 0) return;
  const ppd = ppdRows[0];
  if (ppd.conciliado === 'true') return; // ya conciliada

  // Auto-conciliar la PPD con los mismos datos
  const { rows: inserted } = await pool.query(`
    INSERT INTO conciliaciones (anio, mes, id_cfdi, fecha_de_pago, id_banco)
    VALUES ($1, $2, $3, $4, $5)
    RETURNING id
  `, [anio, mes, ppd.id, fechaDePago, idBanco]);

  await pool.query(`
    UPDATE cfdis SET conciliado = 'true', id_conciliacion = $1 WHERE id = $2
  `, [inserted[0].id, ppd.id]);
}

export async function desconciliar(pool: Pool, conciliacionId: number): Promise<void> {
  // Buscar la conciliacion y el CFDI asociado
  const { rows } = await pool.query(`
    SELECT con.id_cfdi, c.tipo_comprobante, c.uuid_relacionado
    FROM conciliaciones con
    JOIN cfdis c ON c.id = con.id_cfdi
    WHERE con.id = $1
  `, [conciliacionId]);
  if (rows.length === 0) throw new Error('Conciliacion no encontrada');

  const cfdi = rows[0];

  // Desconciliar el CFDI principal
  await pool.query(`UPDATE cfdis SET conciliado = NULL, id_conciliacion = NULL WHERE id_conciliacion = $1`, [conciliacionId]);
  await pool.query(`DELETE FROM conciliaciones WHERE id = $1`, [conciliacionId]);

  // Si es tipo P, también desconciliar la PPD auto-conciliada
  if (cfdi.tipo_comprobante === 'P' && cfdi.uuid_relacionado) {
    const { rows: ppdRows } = await pool.query(`
      SELECT c.id_conciliacion FROM cfdis c
      WHERE c.uuid = $1 AND c.conciliado = 'true' AND c.metodo_pago = 'PPD'
    `, [cfdi.uuid_relacionado]);

    if (ppdRows.length > 0 && ppdRows[0].id_conciliacion) {
      const ppdConcId = ppdRows[0].id_conciliacion;
      await pool.query(`UPDATE cfdis SET conciliado = NULL, id_conciliacion = NULL WHERE id_conciliacion = $1`, [ppdConcId]);
      await pool.query(`DELETE FROM conciliaciones WHERE id = $1`, [ppdConcId]);
    }
  }
}
