import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Logger } from '@nestjs/common';
import type { Job } from 'bullmq';
import { existsSync, mkdirSync, writeFileSync } from 'fs';
import { join } from 'path';
import { MiddlewareSifenService } from 'src/middleware-sifen/middleware-sifen.service';
import { PrismaService } from 'src/prisma/prisma.service';
import type { MiddlewareEmpresaJob } from '../types/middleware-empresa-job.interface';

@Processor('middleware-empresa-queue')
export class MiddlewareEmpresaProcessor extends WorkerHost {
  private readonly logger = new Logger(MiddlewareEmpresaProcessor.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly middlewareSifenService: MiddlewareSifenService,
  ) {
    super();
  }

  private savePayloadToLog(payload: Record<string, unknown>): void {
    try {
      const logsDir = join(process.cwd(), 'logs', 'middleware-empresa');
      if (!existsSync(logsDir)) {
        mkdirSync(logsDir, { recursive: true });
      }
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
      const filename = `payload_empresa_${timestamp}.json`;
      const filepath = join(logsDir, filename);
      writeFileSync(filepath, JSON.stringify(payload, null, 2), 'utf-8');
      this.logger.log(`Payload empresa guardado en: ${filepath}`);
    } catch (error) {
      this.logger.error(`Error guardando payload empresa en log: ${error}`);
    }
  }

  /**
   * Construye el JSON completo que espera el middleware para /empresas/create
   */
  private async buildEmpresaPayload(
    empresaId: string,
    operacion: string,
    origen: string,
  ): Promise<Record<string, unknown>> {
    // 1. Obtener empresa con relaciones
    const empresa = await this.prisma.empresas.findUnique({
      where: { id: empresaId },
      include: {
        tipo_contribuyente_empresas: true,
        actividades_economicas: true,
        empresas_sucursales: {
          include: {
            paises: true,
            departamentos: true,
            distritos: true,
            ciudades: true,
            empresas_puntos_expedicion: true,
          },
        },
        empresas_timbrado: true,
      },
    });

    if (!empresa) {
      throw new Error(`Empresa ${empresaId} no encontrada`);
    }

    // 2. Mapear actividades económicas
    const actividades = (empresa.actividades_economicas || []).map((act) => ({
      usuario: act.usuario || 'admin',
      principal: act.principal ?? false,
      codactividad: act.codactividad || '',
      desc_actividad: act.desc_actividad || '',
    }));

    // 3. Mapear sucursales
    const sucursales = (empresa.empresas_sucursales || []).map((suc) => ({
      email: suc.email || '',
      active: suc.active ?? true,
      pais_id: suc.paises?.codigo || '',
      telefono: suc.telefono || '',
      ciudad_id: suc.ciudades ? String(suc.ciudades.codigo ?? '') : '',
      direccion: suc.direccion || '',
      descripcion: suc.descripcion || '',
      distrito_id: suc.distritos ? String(suc.distritos.codigo ?? '') : '',
      numero_casa: suc.numero_casa || '0',
      complemento_1: suc.complemento_1 || '',
      complemento_2: suc.complemento_2 || '',
      departamento_id: suc.departamentos ? String(suc.departamentos.codigo ?? '') : '',
      punto_establecimiento: suc.punto_establecimiento || '',
      // Logo propio de la sucursal. Vacío = el middleware usa el de la empresa,
      // que es como venía funcionando para todas.
      logo: suc.logo || '',
    }));

    // 4. Mapear timbrados
    const timbrados = (empresa.empresas_timbrado || []).map((tim) => ({
      csc: tim.csc || '',
      id_csc: tim.id_csc || '',
      usuario: tim.usuario || 'admin',
      ambiente: tim.ambiente === 'prod' ? 'prod' : 'test',
      fecha_inicio: tim.fecha_inicio ? tim.fecha_inicio.toISOString().split('T')[0] : '',
      num_timbrado: tim.num_timbrado || '',
    }));

    // 5. Construir payload final
    // Debe coincidir con los parámetros de sp_empresas:
    // (id_x, ruc_x, dv_x, razon_social_x, nombre_fantasia_x, tipo_contribuyente_x,
    //  produccion_x, active_x, usuario_x, actividades_x jsonb, sucursales_x jsonb, timbrados_x jsonb, operacion)
    const tipoContribuyenteCodigo = empresa.tipo_contribuyente_empresas?.codigo ?? 1;
    const produccion = (empresa.empresas_timbrado || []).some((tim) => tim.ambiente === 'prod');
    const data = {
      id: empresa.id,
      ruc: empresa.ruc,
      dv: empresa.dv,
      razon_social: empresa.razon_social || '',
      nombre_fantasia: empresa.nombre_fantasia || '',
      tipo_contribuyente: tipoContribuyenteCodigo,
      produccion,
      active: empresa.active ?? true,
      logo: empresa.logo,
      usuario: 'admin',
      actividades,
      sucursales,
      timbrados,
      operacion,
      objeto: origen,
    };
    return data;
  }

  /**
   * Construye el payload para POST /empresas/create-correo
   * Resuelve credenciales efectivas: propias o system
   */
  private async buildCorreoPayload(empresaId: string): Promise<Record<string, unknown> | null> {
    const empresa = await this.prisma.empresas.findUnique({
      where: { id: empresaId },
      select: { ruc: true, usar_correo_propio: true },
    });
    this.logger.debug(`buildCorreoPayload: empresa=${empresaId} usar_correo_propio=${empresa?.usar_correo_propio}`);

    // Verificar si la empresa ya tiene correo propio (para determinar A o M)
    const correoPropio = await this.prisma.empresas_correo.findFirst({
      where: { empresa_id: empresaId, system: { not: true } },
    });
    const operacion = correoPropio ? 'M' : 'A';
    this.logger.debug(`buildCorreoPayload: correo propio encontrado=${!!correoPropio}, operacion=${operacion}`);

    let correo = null;
    if (empresa?.usar_correo_propio && correoPropio) {
      correo = correoPropio;
    }
    // Fallback: correo system
    if (!correo) {
      correo = await this.prisma.empresas_correo.findFirst({
        where: { system: true },
      });
      this.logger.debug(`buildCorreoPayload: correo system encontrado=${!!correo}`);
    }
    if (!correo) {
      this.logger.warn(`buildCorreoPayload: no se encontró correo para empresa=${empresaId}, omitiendo sync`);
      return null;
    }

    return {
      ruc: empresa?.ruc || '',
      data: {
        empresa: empresaId,
        host: correo.host || '',
        puerto: correo.puerto || '465',
        username: correo.username || '',
        password: correo.password || '',
        email: correo.email || '',
        envio_ssl: correo.envio_ssl ?? true,
        template_personalizado: correo.template_personalizado ?? false,
        adjunto: correo.adjunto ?? true,
        usuario: correo.usuario || 'admin',
        id: correo.id,
      },
      objeto: 'empresas_correo',
      operacion,
    };
  }

  /**
   * Construye el payload para POST /certificates/create-certificate
   */
  private async buildCertificadoPayload(empresaId: string): Promise<Record<string, unknown> | null> {
    const empresa = await this.prisma.empresas.findUnique({
      where: { id: empresaId },
      select: { ruc: true },
    });

    const cert = await this.prisma.certificados.findFirst({
      where: { empresa: empresaId },
      orderBy: { created_at: 'desc' },
    });
    if (!cert) {
      this.logger.warn(`buildCertificadoPayload: no se encontró certificado para empresa=${empresaId}`);
      return null;
    }

    return {
      ruc: empresa?.ruc || '',
      data: {
        empresa: empresaId,
        emisor: cert.emisor || '',
        titular: cert.titular || '',
        fecha_emision: cert.fecha_emision,
        fecha_vencimiento: cert.fecha_vencimiento,
        serialNumber: cert.serial_number || '',
      },
      usuario: 'admin',
      objeto: 'certificados',
      operacion: 'A',
    };
  }

  async process(job: Job<MiddlewareEmpresaJob>): Promise<void> {
    const { empresaId, operacion, origen } = job.data;
    this.logger.log(
      `Processing middleware-empresa job ${job.id} empresa=${empresaId} op=${operacion} origen=${origen}`,
    );

    try {
      // 1. Sincronizar empresa (excepto cuando el origen es correo o certificado)
      if (origen !== 'empresas_correo' && origen !== 'certificados') {
        const payload = await this.buildEmpresaPayload(empresaId, operacion, origen);
        this.savePayloadToLog(payload);
        await this.middlewareSifenService.sendEmpresaToMiddleware(payload);
        this.logger.log(
          `Empresa ${empresaId} sincronizada con middleware (operación: ${operacion}, origen: ${origen})`,
        );
      }

      // 2. Sincronizar correo de notificación
      if (origen !== 'certificados') {
        const correoPayload = await this.buildCorreoPayload(empresaId);
        if (correoPayload) {
          await this.middlewareSifenService.sendCorreoToMiddleware(correoPayload);
          this.logger.log(`Correo empresa ${empresaId} sincronizado con middleware`);
        }
      }

      // 3. Sincronizar certificado digital
      if (origen === 'certificados') {
        const certPayload = await this.buildCertificadoPayload(empresaId);
        if (certPayload) {
          await this.middlewareSifenService.sendCertificadoToMiddleware(certPayload);
          this.logger.log(`Certificado empresa ${empresaId} sincronizado con middleware`);
        }
      }
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error desconocido';
      this.logger.error(`Error sincronizando empresa ${empresaId} con middleware: ${message}`);
      throw error;
    }
  }
}
