import { Credential } from '@nodecfdi/credentials/node';
import type { Pool } from 'pg';
import { encryptFielCredentials, decryptFielCredentials } from './sat/sat-crypto.service.js';
import type { FielStatus } from '@horux/shared';

export async function uploadFielContribuyente(
  pool: Pool,
  contribuyenteId: string,
  cerBase64: string,
  keyBase64: string,
  password: string
): Promise<{ success: boolean; message: string; status?: FielStatus }> {
  try {
    const cerData = Buffer.from(cerBase64, 'base64');
    const keyData = Buffer.from(keyBase64, 'base64');

    let credential: Credential;
    try {
      credential = Credential.create(cerData.toString('binary'), keyData.toString('binary'), password);
    } catch {
      return { success: false, message: 'Los archivos de la FIEL no son válidos o la contraseña es incorrecta' };
    }

    if (!credential.isFiel()) {
      return { success: false, message: 'El certificado proporcionado no es una FIEL (e.firma). Parece ser un CSD.' };
    }

    const certificate = credential.certificate();
    const rfc = certificate.rfc();
    const serialNumber = certificate.serialNumber().bytes();
    const validFrom = new Date(String(certificate.validFromDateTime()));
    const validUntil = new Date(String(certificate.validToDateTime()));

    if (new Date() > validUntil) {
      return { success: false, message: 'La FIEL está vencida desde ' + validUntil.toLocaleDateString() };
    }

    const enc = encryptFielCredentials(cerData, keyData, password);

    // Check whether this contribuyente already had an active FIEL (to decide auto-sync)
    const { rows: existingRows } = await pool.query(
      `SELECT 1 FROM fiel_contribuyente WHERE contribuyente_id = $1 AND is_active = true`,
      [contribuyenteId]
    );
    const isFirstUpload = existingRows.length === 0;

    await pool.query(`
      INSERT INTO fiel_contribuyente (
        contribuyente_id, rfc, cer_data, key_data, key_password_enc,
        cer_iv, cer_tag, key_iv, key_tag, password_iv, password_tag,
        serial_number, valid_from, valid_until, is_active
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, true)
      ON CONFLICT (contribuyente_id) DO UPDATE SET
        rfc = $2, cer_data = $3, key_data = $4, key_password_enc = $5,
        cer_iv = $6, cer_tag = $7, key_iv = $8, key_tag = $9,
        password_iv = $10, password_tag = $11,
        serial_number = $12, valid_from = $13, valid_until = $14,
        is_active = true, updated_at = now()
    `, [
      contribuyenteId, rfc,
      enc.encryptedCer, enc.encryptedKey, enc.encryptedPassword,
      enc.cerIv, enc.cerTag, enc.keyIv, enc.keyTag, enc.passwordIv, enc.passwordTag,
      serialNumber, validFrom, validUntil,
    ]);

    // Trigger auto-sync on first upload (fire-and-forget)
    if (isFirstUpload) {
      import('./opinion-cumplimiento.service.js').then(async ({ consultarOpinionContribuyente }) => {
        try {
          await consultarOpinionContribuyente(pool, contribuyenteId);
        } catch (err: any) {
          console.error(`[FIEL first-upload] Opinión falló para contribuyente ${contribuyenteId}:`, err.message || err);
        }
      }).catch(() => {});

      import('./constancia.service.js').then(async ({ consultarConstanciaContribuyente }) => {
        try {
          await consultarConstanciaContribuyente(pool, contribuyenteId);
        } catch (err: any) {
          console.error(`[FIEL first-upload] CSF falló para contribuyente ${contribuyenteId}:`, err.message || err);
        }
      }).catch(() => {});
    }

    const daysUntilExpiration = Math.ceil((validUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24));

    return {
      success: true,
      message: 'FIEL configurada correctamente',
      status: { configured: true, rfc, serialNumber, validFrom: validFrom.toISOString(), validUntil: validUntil.toISOString(), isExpired: false, daysUntilExpiration },
    };
  } catch (error: any) {
    console.error('[FIEL Contribuyente Upload Error]', error);
    return { success: false, message: error.message || 'Error al procesar la FIEL' };
  }
}

export async function getFielStatusContribuyente(pool: Pool, contribuyenteId: string): Promise<FielStatus> {
  // Try per-contribuyente first (tenant BD)
  const { rows } = await pool.query(`
    SELECT rfc, serial_number AS "serialNumber", valid_from AS "validFrom", valid_until AS "validUntil", is_active AS "isActive"
    FROM fiel_contribuyente WHERE contribuyente_id = $1
  `, [contribuyenteId]);

  if (rows.length === 0 || !rows[0].isActive) {
    // Fallback: check legacy tenant-level FIEL by matching RFC
    const { rows: contribRows } = await pool.query('SELECT rfc FROM contribuyentes WHERE entidad_id = $1', [contribuyenteId]);
    const rfc = contribRows[0]?.rfc;
    if (rfc) {
      const { getFielStatus } = await import('./fiel.service.js');
      // getFielStatus reads by tenantId — check if the legacy FIEL matches this RFC
      // We need prisma access, so import it
      const { prisma } = await import('../config/database.js');
      const legacyFiel = await prisma.fielCredential.findFirst({
        where: { rfc, isActive: true },
        select: { rfc: true, serialNumber: true, validFrom: true, validUntil: true, isActive: true },
      });
      if (legacyFiel) {
        const now = new Date();
        const isExpired = now > legacyFiel.validUntil;
        const daysUntilExpiration = Math.ceil((legacyFiel.validUntil.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
        return {
          configured: true,
          rfc: legacyFiel.rfc,
          serialNumber: legacyFiel.serialNumber || undefined,
          validFrom: legacyFiel.validFrom.toISOString(),
          validUntil: legacyFiel.validUntil.toISOString(),
          isExpired,
          daysUntilExpiration: isExpired ? 0 : daysUntilExpiration,
        };
      }
    }
    return { configured: false };
  }

  const fiel = rows[0];
  const now = new Date();
  const isExpired = now > new Date(fiel.validUntil);
  const daysUntilExpiration = Math.ceil((new Date(fiel.validUntil).getTime() - now.getTime()) / (1000 * 60 * 60 * 24));

  return {
    configured: true,
    rfc: fiel.rfc,
    serialNumber: fiel.serialNumber || undefined,
    validFrom: new Date(fiel.validFrom).toISOString(),
    validUntil: new Date(fiel.validUntil).toISOString(),
    isExpired,
    daysUntilExpiration: isExpired ? 0 : daysUntilExpiration,
  };
}

export async function getDecryptedFielContribuyente(pool: Pool, contribuyenteId: string): Promise<{
  cerContent: string; keyContent: string; password: string; rfc: string;
} | null> {
  const { rows } = await pool.query(`
    SELECT * FROM fiel_contribuyente WHERE contribuyente_id = $1 AND is_active = true
  `, [contribuyenteId]);

  if (rows.length === 0) {
    // Fallback: check legacy FIEL by matching RFC
    const { rows: contribRows } = await pool.query('SELECT rfc FROM contribuyentes WHERE entidad_id = $1', [contribuyenteId]);
    const rfc = contribRows[0]?.rfc;
    if (rfc) {
      const { prisma } = await import('../config/database.js');
      const legacyFiel = await prisma.fielCredential.findFirst({
        where: { rfc, isActive: true },
      });
      if (legacyFiel && new Date() <= legacyFiel.validUntil) {
        try {
          const { decryptFielCredentials } = await import('./sat/sat-crypto.service.js');
          const { cerData, keyData, password } = decryptFielCredentials(
            Buffer.from(legacyFiel.cerData), Buffer.from(legacyFiel.keyData), Buffer.from(legacyFiel.keyPasswordEncrypted),
            Buffer.from(legacyFiel.cerIv), Buffer.from(legacyFiel.cerTag),
            Buffer.from(legacyFiel.keyIv), Buffer.from(legacyFiel.keyTag),
            Buffer.from(legacyFiel.passwordIv), Buffer.from(legacyFiel.passwordTag)
          );
          return { cerContent: cerData.toString('binary'), keyContent: keyData.toString('binary'), password, rfc: legacyFiel.rfc };
        } catch (err) {
          console.error('[FIEL Contribuyente] Legacy decrypt failed:', err);
          return null;
        }
      }
    }
    return null;
  }
  const fiel = rows[0];

  if (new Date() > new Date(fiel.valid_until)) return null;

  try {
    const { cerData, keyData, password } = decryptFielCredentials(
      Buffer.from(fiel.cer_data), Buffer.from(fiel.key_data), Buffer.from(fiel.key_password_enc),
      Buffer.from(fiel.cer_iv), Buffer.from(fiel.cer_tag),
      Buffer.from(fiel.key_iv), Buffer.from(fiel.key_tag),
      Buffer.from(fiel.password_iv), Buffer.from(fiel.password_tag)
    );
    return { cerContent: cerData.toString('binary'), keyContent: keyData.toString('binary'), password, rfc: fiel.rfc };
  } catch (error) {
    console.error('[FIEL Contribuyente Decrypt Error]', error);
    return null;
  }
}
