import { BadRequestException, ConflictException, ForbiddenException, Injectable, NotFoundException, Logger } from '@nestjs/common';
import type { LoginUserInfo } from 'src/auth/dto/login.dto';
import * as fs from 'fs';
import * as nodemailer from 'nodemailer';
import * as path from 'path';
import { AuditService } from 'src/audit/audit.service';
import { envs } from 'src/config';
import { formatDateResponse } from 'src/utils/utilidades';
import { PrismaService } from '../prisma/prisma.service';
import { CreateEmpresaFullDto } from './dto/create-empresa-full.dto';
import { UpsertEmpresaCorreoDto } from './dto/empresas-correo.dto';
import { UpdateEmpresaDto } from './dto/update-empresa.dto';

import * as forge from 'node-forge';
import { QueuesService } from 'src/queues/queues.service';
import { PlanLimitsService } from '../plan-limits/plan-limits.service';
import { SuscripcionesService } from '../suscripciones/suscripciones.service';
import { ComisionesService } from './comisiones.service';
import { JerarquiaEmpresasService } from './jerarquia-empresas.service';
import { RegistroEmpresaService } from './registro-empresa.service';

@Injectable()
export class EmpresasService {
  private readonly logger = new Logger(EmpresasService.name);
  constructor(
    private prisma: PrismaService,
    private suscripcionesService: SuscripcionesService,
    private comisionesService: ComisionesService,
    private jerarquiaService: JerarquiaEmpresasService,
    private queuesService: QueuesService,
    private planLimitsService: PlanLimitsService,
    private readonly registroEmpresaService: RegistroEmpresaService,
    private readonly auditService: AuditService,
  ) {}

  private toStringOrUndefined(value: unknown): string | undefined {
    if (value === null || value === undefined) return undefined;
    if (typeof value === 'string') return value;
    if (Array.isArray(value)) return value.map((v) => String(v)).join(' ');
    if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value);
    try {
      const asJson = JSON.stringify(value);
      return typeof asJson === 'string' ? asJson : undefined;
    } catch {
      return undefined;
    }
  }

  private validatePin(certBuffer: Buffer, pin: string) {
    try {
      const p12Asn1 = forge.asn1.fromDer(certBuffer.toString('binary'));
      forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, pin);
      return { valid: true } as const;
    } catch {
      return { valid: false, message: 'PIN incorrecto' } as const;
    }
  }

  private getCertificateData(certBuffer: Buffer, pin: string) {
    const p12Asn1 = forge.asn1.fromDer(certBuffer.toString('binary'));
    const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, pin);

    const bags = p12.getBags({ bagType: forge.pki.oids.certBag });
    const certBag = bags?.[forge.pki.oids.certBag]?.[0];
    const certificate = certBag?.cert;
    if (!certificate) {
      throw new Error('No se pudo leer el certificado');
    }

    const issuerCnRaw = certificate.issuer.attributes.find((attr) => attr.shortName === 'CN')?.value;
    const subjectCnRaw = certificate.subject.attributes.find((attr) => attr.shortName === 'CN')?.value;
    const issuerCn = this.toStringOrUndefined(issuerCnRaw);
    const subjectCn = this.toStringOrUndefined(subjectCnRaw);
    const serialNumber = this.toStringOrUndefined(certificate.serialNumber);

    return {
      emisor: issuerCn,
      titular: subjectCn,
      fecha_emision: certificate.validity?.notBefore,
      fecha_vencimiento: certificate.validity?.notAfter,
      serialNumber,
      certificate,
      p12,
    } as const;
  }

  private extractKeysToPem(p12: forge.pkcs12.Pkcs12Pfx) {
    const keyBags = p12.getBags({
      bagType: forge.pki.oids.pkcs8ShroudedKeyBag,
    });
    const shrouded = keyBags?.[forge.pki.oids.pkcs8ShroudedKeyBag]?.[0]?.key;
    if (shrouded) {
      return { privateKeyPem: this.privateKeyToPkcs8Pem(shrouded) };
    }

    const keyBags2 = p12.getBags({ bagType: forge.pki.oids.keyBag });
    const key = keyBags2?.[forge.pki.oids.keyBag]?.[0]?.key;
    if (!key) {
      throw new Error('No se pudo extraer la llave privada');
    }
    return { privateKeyPem: this.privateKeyToPkcs8Pem(key) };
  }

  private privateKeyToPkcs8Pem(key: forge.pki.PrivateKey): string {
    const rsaAsn1 = forge.pki.privateKeyToAsn1(key);
    const privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaAsn1);
    return forge.pki.privateKeyInfoToPem(privateKeyInfo);
  }

  private persistCertificateArtifacts(params: {
    empresaRuc: string;
    certBuffer: Buffer;
    certPem: string;
    privateKeyPem: string;
  }) {
    const basePath = (envs.pathCert ?? '').trim();
    if (!basePath) return;

    const certFolderPath = path.join(basePath, params.empresaRuc);
    fs.mkdirSync(certFolderPath, { recursive: true, mode: 0o700 });

    const certP12Path = path.join(certFolderPath, 'cert.p12');
    const privateKeyPath = path.join(certFolderPath, 'privatekey.pem');
    const publicKeyPath = path.join(certFolderPath, 'publickey.pem');

    fs.writeFileSync(certP12Path, params.certBuffer);
    fs.writeFileSync(privateKeyPath, params.privateKeyPem, { mode: 0o600 });
    fs.writeFileSync(publicKeyPath, params.certPem, { mode: 0o600 });
  }

  // async createTimbrado(createTimbradoDto: CreateTimbradoDto, empresa_id: string) {
  //   try {
  //     const empresa = await this.prisma.empresas.findUnique({
  //       where: { id: empresa_id },
  //     });
  //     const fecha_inicio = new Date(createTimbradoDto.fecha_inicio);
  //     if (!empresa) throw new NotFoundException('Empresa no encontrada');

  //     const timbradoEmpresa = await this.prisma.empresas_timbrado.findFirst({
  //       where: {
  //         empresa_id: empresa_id,
  //         num_timbrado: createTimbradoDto.num_timbrado,
  //       },
  //     });

  //     if (timbradoEmpresa) throw new ConflictException('Ya existe un timbrado para esta empresa');

  //     const timbrado = await this.prisma.empresas_timbrado.create({
  //       data: {
  //         ...createTimbradoDto,
  //         fecha_inicio,
  //       },
  //     });

  //     return { message: 'Timbrado creado correctamente', data: timbrado };
  //   } catch (error) {
  //     if (error instanceof ConflictException) throw error;
  //     if (error instanceof NotFoundException) throw error;

  //     const message = error instanceof Error ? error.message : 'Error desconocido';
  //     throw new Error(`Error al crear timbrado: ${message}`);
  //   }

  async changePlan(
    empresaId: string,
    planId: string,
    fechaInicio?: Date,
    motivo?: string,
    userId?: string,
    fechaFin?: Date,
    costoMensual?: number,
    diaCobro?: number,
  ) {
    await this.findOne(empresaId);

    // Obtener plan anterior para calcular comisiones de upgrade
    const suscripcionActual = await this.prisma.suscripciones.findFirst({
      where: { empresa_id: empresaId, estado: 'Activo' },
      select: { id: true, plan_id: true },
    });

    const result = await this.suscripcionesService.changePlan(
      empresaId,
      planId,
      fechaInicio,
      motivo,
      userId,
      fechaFin,
      costoMensual,
      diaCobro,
    );

    // Generar comisiones automáticamente si es un upgrade y hay parent
    if (suscripcionActual && suscripcionActual.plan_id !== planId) {
      try {
        await this.comisionesService.procesarComisionUpgrade(
          suscripcionActual.id,
          suscripcionActual.plan_id || '',
          planId,
        );
      } catch (error) {
        // No fallar el cambio de plan por errores de comisiones
      }
    }

    return result;
  }

  async applyOverride(
    empresaId: string,
    overrideLimites: Record<string, number>,
    expira_at: Date | null,
    nota: string | null,
    userId?: string,
  ) {
    await this.findOne(empresaId);

    const suscripcionActual = await this.prisma.suscripciones.findFirst({
      where: { empresa_id: empresaId, estado: 'Activo' },
      select: { id: true, limite_override: true, override_expira_at: true, override_nota: true },
    });

    const result = await this.planLimitsService.applyOverride(empresaId, overrideLimites, expira_at, nota);

    const camposModificados = Object.keys(overrideLimites).join(', ');
    const vencimiento = expira_at
      ? expira_at.toLocaleDateString('es-PY', { day: '2-digit', month: 'short', year: 'numeric' })
      : 'Sin vencimiento';

    await this.auditService.log({
      empresa_id: empresaId,
      user_id: userId,
      action: 'APLICAR_OVERRIDE_LIMITES',
      entity_type: 'suscripcion',
      entity_id: result.suscripcion_id,
      old_value: suscripcionActual?.limite_override
        ? {
            limite_override: suscripcionActual.limite_override as Record<string, unknown>,
            override_expira_at: suscripcionActual.override_expira_at ?? null,
            override_nota: suscripcionActual.override_nota ?? null,
          }
        : undefined,
      new_value: {
        limite_override: overrideLimites,
        override_expira_at: expira_at ?? null,
        override_nota: nota ?? null,
      },
      descripcion: [
        `Límites ampliados: ${camposModificados}`,
        nota ? `Nota: ${nota}` : null,
        `Vencimiento: ${vencimiento}`,
        suscripcionActual?.limite_override ? '(sobreescribió override previo)' : null,
      ]
        .filter(Boolean)
        .join(' — '),
    });

    return result;
  }

  async clearOverride(empresaId: string, userId?: string) {
    await this.findOne(empresaId);

    const suscripcionActual = await this.prisma.suscripciones.findFirst({
      where: { empresa_id: empresaId, estado: 'Activo' },
      select: { id: true, limite_override: true, override_expira_at: true, override_nota: true },
    });

    const result = await this.planLimitsService.clearOverride(empresaId);

    await this.auditService.log({
      empresa_id: empresaId,
      user_id: userId,
      action: 'ELIMINAR_OVERRIDE_LIMITES',
      entity_type: 'suscripcion',
      entity_id: suscripcionActual?.id,
      old_value: suscripcionActual?.limite_override
        ? {
            limite_override: suscripcionActual.limite_override as Record<string, unknown>,
            override_expira_at: suscripcionActual.override_expira_at ?? null,
            override_nota: suscripcionActual.override_nota ?? null,
          }
        : undefined,
      new_value: { limite_override: null },
      descripcion: 'Override de límites eliminado — se restauraron los límites del plan',
    });

    return result;
  }

  async uploadCertificadoDigital(params: {
    empresaId: string;
    usuario: string;
    pin: string;
    fileBuffer: Buffer;
    originalFilename: string;
  }): Promise<unknown> {
    await this.findOne(params.empresaId);

    const pinValidation = this.validatePin(params.fileBuffer, params.pin);
    if (!pinValidation.valid) {
      throw new BadRequestException(pinValidation.message);
    }

    const certData = this.getCertificateData(params.fileBuffer, params.pin);

    const empresa = await this.prisma.empresas.findUnique({
      where: { id: params.empresaId },
      select: { ruc: true },
    });
    if (!empresa?.ruc) {
      throw new NotFoundException('Empresa no encontrada');
    }
    let record;
    try {
      record = await this.prisma.certificados.create({
        data: {
          empresa: params.empresaId,
          emisor: this.toStringOrUndefined(certData.emisor),
          titular: this.toStringOrUndefined(certData.titular),
          fecha_emision: certData.fecha_emision,
          fecha_vencimiento: certData.fecha_vencimiento,
          serial_number: certData.serialNumber,
          usuario: params.usuario,
        },
      });
    } catch (error) {
      const msg = error instanceof Error ? error.message : 'Error al guardar la información del certificado';
      if (msg.toLowerCase().includes('unique') || msg.toLowerCase().includes('serial')) {
        throw new ConflictException('El certificado ya existe');
      }
      throw error;
    }

    const { privateKeyPem } = this.extractKeysToPem(certData.p12);
    const certPem = forge.pki.certificateToPem(certData.certificate);

    this.persistCertificateArtifacts({
      empresaRuc: empresa.ruc,
      certBuffer: params.fileBuffer,
      certPem,
      privateKeyPem,
    });

    // Sincronizar certificado con middleware
    try {
      await this.queuesService.enqueueMiddlewareEmpresa(params.empresaId, 'M', 'certificados');
    } catch (e) {
      this.logger.warn(`No se pudo encolar sync certificado middleware: ${e}`, 'EmpresasService');
    }

    return {
      status: 'success',
      message: 'Certificado validado exitósamente',
      data: {
        serialNumber: record.serial_number,
        titular: record.titular,
      },
    };
  }
  async create(dto: CreateEmpresaFullDto, creadorUserId?: string) {
    try {
      const result = await this.registroEmpresaService.registrar({
        ruc: dto.ruc,
        dv: dto.dv,
        razon_social: dto.razon_social,
        nombre_fantasia: dto.nombre_fantasia,
        tipo_contribuyente: dto.tipo_contribuyente,
        celular: dto.celular,
        email: dto.email,
        usuario: dto.usuario,
        password: dto.password,
        nombres_usuario: dto.nombres_usuario,
        plan_id: dto.plan_id,
        parent_id: dto.parent_id,
        type: dto.type,
        precio_acordado: (dto as any).precio_acordado,
        fecha_inicio_plan: (dto as any).fecha_inicio_plan,
        dia_cobro: (dto as any).dia_cobro,
        notas_comerciales: (dto as any).notas_comerciales,
      });

      // Sincronizar con middleware (Alta)
      await this.queuesService.enqueueMiddlewareEmpresa(result.empresa.id, 'A', 'empresas');

      this.logger.log(`Empresa creada: ${dto.razon_social} (${result.empresa.id})`, 'EmpresasService');
      await this.auditService.log({
        empresa_id: result.empresa.id,
        user_id: creadorUserId,
        action: 'CREATE',
        entity_type: 'empresa',
        entity_id: result.empresa.id,
        descripcion: `Empresa registrada: ${dto.razon_social} | RUC: ${dto.ruc} | Usuario: ${dto.usuario}`,
        new_value: { razon_social: dto.razon_social, ruc: dto.ruc, type: result.empresa.type, usuario: dto.usuario },
      });

      return {
        empresa_id: result.empresa.id,
        usuario_id: result.usuario.id,
        razon_social: dto.razon_social,
        ruc: dto.ruc,
        usuario: dto.usuario,
      };
    } catch (error) {
      if (error.status) throw error;
      const message = error instanceof Error ? error.message : 'Error desconocido';
      throw new BadRequestException(`Error al crear empresa: ${message}`);
    }
  }

  async findAll(page = 1, limit = 10) {
    const take = Math.max(1, Math.min(100, Number(limit) || 10));
    const currentPage = Math.max(1, Number(page) || 1);
    const skip = (currentPage - 1) * take;

    const [items, total] = await this.prisma.$transaction([
      this.prisma.empresas.findMany({
        select: {
          id: true,
          ruc: true,
          dv: true,
          razon_social: true,
          nombre_fantasia: true,
          celular: true,
          email: true,
          active: true,
          estado_empresa: true,
          tipo_contribuyente_empresas: true,
          suscripciones: true,
          produccion: true,
          parent_id: true,
          type: true,
          empresas: { select: { id: true, razon_social: true } },
          other_empresas: {
            select: { id: true, razon_social: true, ruc: true, active: true },
          },
          created_at: true,
          updated_at: true,
        },
        skip,
        take,
        orderBy: { created_at: 'desc' },
      }),
      this.prisma.empresas.count(),
    ]);

    return {
      items,
      total,
      page: currentPage,
      limit: take,
      lastPage: Math.max(1, Math.ceil(total / take) || 1),
    };
  }

  /**
   * Valida que el usuario pueda leer una empresa distinta a la propia.
   *
   * Orden de chequeos (cualquiera pasa):
   *  1. SuperAdmin → siempre.
   *  2. La empresa target es la original del usuario (empresa_id) o una
   *     empresa accesible (vía usuario_empresas / hijas en superadmin /
   *     parent_id si es hija de la propia). Esto cubre el boot del frontend
   *     y el período de switch entre empresas asignadas, donde localStorage
   *     y JWT pueden quedar desincronizados por un tick.
   *  3. Caso final: exigir el privilegio ADM_EMP_EMPRESA_VER.
   */
  async requirePermisoLecturaOtraEmpresa(user: LoginUserInfo, targetEmpresaId: string) {
    const usuario = await this.prisma.usuario.findUnique({
      where: { id: user.id },
      select: { is_superadmin: true, empresa_id: true },
    });
    if (usuario?.is_superadmin) return;

    // (2) Empresa original del usuario
    if (usuario?.empresa_id === targetEmpresaId) return;

    // (2) Empresa asignada explícitamente en usuario_empresas
    const asignacion = await this.prisma.usuario_empresas.findFirst({
      where: { usuario_id: user.id, empresa_id: targetEmpresaId, active: true },
      select: { id: true },
    });
    if (asignacion) return;

    // (2) Empresa hija de la empresa original (jerarquía parent_id)
    if (usuario?.empresa_id) {
      const hija = await this.prisma.empresas.findFirst({
        where: { id: targetEmpresaId, parent_id: usuario.empresa_id, active: true },
        select: { id: true },
      });
      if (hija) return;
    }

    // (3) Fallback: privilegio explícito
    const tienePermiso = await this.prisma.perfiles_privilegios.findFirst({
      where: {
        active: true,
        modulos: { codigo: 'ADMINISTRACION' },
        privilegios: { codigo: 'ADM_EMP_EMPRESA_VER' },
        perfiles: {
          usuario_perfiles: { some: { user_id: user.id } },
        },
      },
      select: { id: true },
    });
    if (!tienePermiso) {
      throw new ForbiddenException(
        `No tiene permiso "ADM_EMP_EMPRESA_VER" para leer la empresa ${targetEmpresaId}`,
      );
    }
  }

  async findOne(id: string) {
    const empresa = await this.prisma.empresas.findUnique({
      where: { id },
      select: {
        id: true,
        ruc: true,
        dv: true,
        razon_social: true,
        nombre_fantasia: true,
        tipo_contribuyente: true,
        celular: true,
        celular_validado: true,
        email: true,
        email_validado: true,
        active: true,
        produccion: true,
        parent_id: true,
        type: true,
        logo: true,
        configuracion_precios: true,
        orden_requiere_aprobacion: true,
        orden_monto_aprobacion: true,
        presupuesto_facturar_sin_ov: true,
        actividad_economica_kude: true,
        sifen_activo: true,
        imputa_iva: true,
        imputa_ire: true,
        imputa_irp_rsp: true,
        empresas: { select: { id: true, razon_social: true, ruc: true } },
        other_empresas: {
          select: {
            id: true,
            razon_social: true,
            ruc: true,
            active: true,
            type: true,
          },
        },
        created_at: true,
        updated_at: true,
        empresas_sucursales: true,
        estado_empresa: true,
        actividades_economicas: true,
        tipo_contribuyente_empresas: true,
        empresas_timbrado: true,
        empresas_puntos_expedicion: {
          select: {
            id: true,
            empresa_id: true,
            sucursal_id: true,
            caja_id: true,
            punto_expedicion: true,
            num_inicial: true,
            descripcion: true,
            eslogan: true,
            active: true,
            created_at: true,
            updated_at: true,
            numeraciones_documento: {
              select: {
                id: true,
                tipo_documento_id: true,
                numero_actual: true,
                active: true,
                tipo_documento: {
                  select: {
                    id: true,
                    descripcion: true,
                    abreviatura: true,
                  },
                },
              },
            },
          },
        },
        empresas_correo: true,
        certificados: true,
        suscripciones: {
          select: {
            id: true,
            plan_id: true,
            fecha_inicio: true,
            fecha_fin: true,
            estado: true,
            fecha_prox_cobro: true,
            fecha_cancelacion: true,
            descripcion: true,
            costo_mensual: true,
            created_at: true,
            updated_at: true,
            suscripcion_modulos: true,
            planes: {
              select: {
                id: true,
                codigo: true,
                descripcion: true,
                precio: true,
                periodo_prueba: true,
              },
            },
          },
        },
      },
    });

    if (!empresa) throw new NotFoundException('Empresa no encontrada');

    // Transformar fechas a formato string
    const empresaTransformada = {
      ...empresa,
      created_at: formatDateResponse(empresa.created_at),
      updated_at: formatDateResponse(empresa.updated_at),
      empresas_timbrado: empresa.empresas_timbrado?.map((timbrado) => ({
        ...timbrado,
        fecha_inicio: formatDateResponse(timbrado.fecha_inicio, true),
        created_at: formatDateResponse(timbrado.created_at),
        updated_at: formatDateResponse(timbrado.updated_at),
      })),
      certificados: empresa.certificados?.map((cert) => ({
        ...cert,
        fecha_emision: formatDateResponse(cert.fecha_emision),
        fecha_vencimiento: formatDateResponse(cert.fecha_vencimiento),
        created_at: formatDateResponse(cert.created_at),
        updated_at: formatDateResponse(cert.updated_at),
      })),
      suscripciones: empresa.suscripciones?.map((sub) => ({
        ...sub,
        fecha_inicio: formatDateResponse(sub.fecha_inicio, true),
        fecha_fin: formatDateResponse(sub.fecha_fin, true),
        fecha_prox_cobro: formatDateResponse(sub.fecha_prox_cobro, true),
        fecha_cancelacion: formatDateResponse(sub.fecha_cancelacion, true),
        created_at: formatDateResponse(sub.created_at),
        updated_at: formatDateResponse(sub.updated_at),
      })),
    };

    return empresaTransformada;
  }

  async findByRuc(ruc: string) {
    const empresa = await this.prisma.empresas.findUnique({
      where: { ruc },
    });

    if (!empresa) {
      throw new NotFoundException('Empresa no encontrada');
    }

    return empresa;
  }

  async update(id: string, updateEmpresaDto: UpdateEmpresaDto, user_id?: string) {
    const empresaExistente = await this.findOne(id);

    try {
      // Verificar si el RUC ya existe en otra empresa
      if (updateEmpresaDto.ruc) {
        const existingRuc = await this.prisma.empresas.findFirst({
          where: {
            ruc: updateEmpresaDto.ruc,
            id: { not: id },
          },
        });
        if (existingRuc) {
          throw new ConflictException('Ya existe otra empresa con este RUC');
        }
      }

      if (updateEmpresaDto.estado_id) {
        const estado = await this.prisma.estado_empresa.findFirst({
          where: { id: updateEmpresaDto.estado_id },
        });
        if (!estado) throw new NotFoundException('Estado no encontrado');
      }

      let tipo_contribuyente_id: string | undefined;
      if (updateEmpresaDto.tipo_contribuyente !== undefined && updateEmpresaDto.tipo_contribuyente !== null) {
        const tipo_contribuyente = await this.prisma.tipo_contribuyente.findFirst({
          where: { codigo: updateEmpresaDto.tipo_contribuyente },
        });
        if (!tipo_contribuyente) throw new NotFoundException('Tipo contribuyente no encontrado');
        tipo_contribuyente_id = tipo_contribuyente.id;
      }

      // Validar parent_id si se proporciona
      if (updateEmpresaDto.parent_id) {
        if (updateEmpresaDto.parent_id === id) {
          throw new ConflictException('Una empresa no puede ser su propio padre');
        }
        const parentEmpresa = await this.prisma.empresas.findUnique({
          where: { id: updateEmpresaDto.parent_id },
        });
        if (!parentEmpresa) throw new NotFoundException('Empresa padre no encontrada');
      }

      const empresa = await this.prisma.empresas.update({
        where: { id },
        data: {
          ruc: updateEmpresaDto.ruc,
          dv: updateEmpresaDto.dv,
          razon_social: updateEmpresaDto.razon_social,
          nombre_fantasia: updateEmpresaDto.nombre_fantasia,
          ...(tipo_contribuyente_id ? { tipo_contribuyente: tipo_contribuyente_id } : {}),
          celular: updateEmpresaDto.celular,
          email: updateEmpresaDto.email,
          estado_id: updateEmpresaDto.estado_id,
          parent_id: updateEmpresaDto.parent_id !== undefined ? updateEmpresaDto.parent_id : undefined,
          type: updateEmpresaDto.type !== undefined ? updateEmpresaDto.type : undefined,
          logo: updateEmpresaDto.logo !== undefined ? updateEmpresaDto.logo : undefined,
          configuracion_precios:
            updateEmpresaDto.configuracion_precios !== undefined
              ? (updateEmpresaDto.configuracion_precios as any)
              : undefined,
          orden_requiere_aprobacion: updateEmpresaDto.orden_requiere_aprobacion !== undefined ? updateEmpresaDto.orden_requiere_aprobacion : undefined,
          orden_monto_aprobacion: updateEmpresaDto.orden_monto_aprobacion !== undefined ? updateEmpresaDto.orden_monto_aprobacion : undefined,
          presupuesto_facturar_sin_ov: updateEmpresaDto.presupuesto_facturar_sin_ov !== undefined ? updateEmpresaDto.presupuesto_facturar_sin_ov : undefined,
          actividad_economica_kude:
            updateEmpresaDto.actividad_economica_kude !== undefined
              ? (updateEmpresaDto.actividad_economica_kude?.trim() || null)
              : undefined,
          sifen_activo:
            updateEmpresaDto.sifen_activo !== undefined
              ? updateEmpresaDto.sifen_activo
              : undefined,
          imputa_iva:
            updateEmpresaDto.imputa_iva !== undefined ? updateEmpresaDto.imputa_iva : undefined,
          imputa_ire:
            updateEmpresaDto.imputa_ire !== undefined ? updateEmpresaDto.imputa_ire : undefined,
          imputa_irp_rsp:
            updateEmpresaDto.imputa_irp_rsp !== undefined ? updateEmpresaDto.imputa_irp_rsp : undefined,
          updated_at: new Date(),
        },
      });
      // Solo actualizar actividades económicas si se proporcionan explícitamente
      if (updateEmpresaDto.actividad_economicas !== undefined) {
        await this.prisma.actividades_economicas.deleteMany({
          where: { empresa_id: id },
        });
        if (updateEmpresaDto.actividad_economicas && updateEmpresaDto.actividad_economicas.length > 0) {
          await this.prisma.actividades_economicas.createMany({
            data: updateEmpresaDto.actividad_economicas.map((actividad_economica) => ({
              empresa_id: id,
              principal: actividad_economica.principal,
              desc_actividad: actividad_economica.desc_actividad,
              codactividad: actividad_economica.codactividad,
            })),
          });
        }
      }

      const timbradoPayload = updateEmpresaDto.timbrado;

      if (timbradoPayload?.num_timbrado) {
        const timbradoExistente = await this.prisma.empresas_timbrado.findFirst({
          where: {
            empresa_id: id,
            num_timbrado: timbradoPayload.num_timbrado,
          },
        });

        const fecha_inicio = timbradoPayload.fecha_inicio ? new Date(timbradoPayload.fecha_inicio) : undefined;

        if (timbradoExistente) {
          await this.prisma.empresas_timbrado.update({
            where: { id: timbradoExistente.id },
            data: {
              id_csc: timbradoPayload.id_csc,
              csc: timbradoPayload.csc,
              ambiente: timbradoPayload.ambiente,
              ...(fecha_inicio ? { fecha_inicio } : {}),
              updated_at: new Date(),
            },
          });
        } else {
          await this.prisma.empresas_timbrado.create({
            data: {
              empresa_id: id,
              num_timbrado: timbradoPayload.num_timbrado,
              id_csc: timbradoPayload.id_csc,
              csc: timbradoPayload.csc,
              ambiente: timbradoPayload.ambiente,
              ...(fecha_inicio ? { fecha_inicio } : {}),
            },
          });
        }
      }

      // Sincronizar con middleware (Modificar)
      await this.queuesService.enqueueMiddlewareEmpresa(id, 'M', 'empresas');

      this.logger.log(`Empresa actualizada: ${empresa.razon_social} (${id})`, 'EmpresasService');
      await this.auditService.log({
        empresa_id: id,
        user_id,
        action: 'UPDATE',
        entity_type: 'empresa',
        entity_id: id,
        descripcion: `Empresa actualizada: ${empresa.razon_social}`,
        old_value: { razon_social: empresaExistente.razon_social, ruc: empresaExistente.ruc },
        new_value: { razon_social: empresa.razon_social, ruc: empresa.ruc },
      });
      return empresa;
    } catch (error) {
      console.log(error);
      const message = error instanceof Error ? error.message : 'Error desconocido';
      throw new Error(`Error al actualizar empresa: ${message}`);
    }
  }

  async getEstadosEmpresa() {
    return this.prisma.estado_empresa.findMany({
      where: { activo: true },
      select: { id: true, codigo: true, descripcion: true },
      orderBy: { codigo: 'asc' },
    });
  }

  async cambiarEstadoEmpresa(empresaId: string, estadoId: string, userId?: string) {
    const empresa = await this.prisma.empresas.findUnique({ where: { id: empresaId } });
    if (!empresa) throw new NotFoundException('Empresa no encontrada');

    const estado = await this.prisma.estado_empresa.findUnique({ where: { id: estadoId } });
    if (!estado) throw new NotFoundException('Estado no encontrado');

    const oldEstado = empresa.estado_id;

    // Determinar si la empresa debe estar activa según el estado
    const estadosActivos = ['1', '2', '3']; // Pendiente, En verificación, Activo
    const isActive = estadosActivos.includes(estado.codigo ?? '');

    const updated = await this.prisma.empresas.update({
      where: { id: empresaId },
      data: {
        estado_id: estadoId,
        active: isActive,
        updated_at: new Date(),
      },
      include: { estado_empresa: true },
    });

    this.logger.log(`Estado empresa ${empresaId} cambiado a ${estado.descripcion}`, 'EmpresasService');
    await this.auditService.log({
      empresa_id: empresaId,
      user_id: userId,
      action: 'UPDATE',
      entity_type: 'empresa',
      entity_id: empresaId,
      descripcion: `Estado cambiado a: ${estado.descripcion}`,
      old_value: { estado_id: oldEstado },
      new_value: { estado_id: estadoId, estado: estado.descripcion },
    });

    return updated;
  }

  async remove(id: string, user_id?: string) {
    const empresaExistente = await this.findOne(id);

    // Soft delete - marcar como inactiva en lugar de eliminar
    const empresa = await this.prisma.empresas.update({
      where: { id },
      data: {
        active: false,
        updated_at: new Date(),
      },
    });

    // Sincronizar con middleware (Baja)
    await this.queuesService.enqueueMiddlewareEmpresa(id, 'B', 'empresas');

    this.logger.log(`Empresa desactivada: ${empresaExistente.razon_social} (${id})`, 'EmpresasService');
    await this.auditService.log({
      empresa_id: id,
      user_id,
      action: 'DEACTIVATE',
      entity_type: 'empresa',
      entity_id: id,
      descripcion: `Empresa desactivada: ${empresaExistente.razon_social}`,
      old_value: { active: true },
      new_value: { active: false },
    });
    return { message: 'Empresa eliminada correctamente', empresa };
  }

  async forceDeleteEmpresa(targetId: string, callerEmpresaId: string, user_id?: string) {
    // Validar que la empresa target existe
    const target = await this.prisma.empresas.findUnique({ where: { id: targetId } });
    if (!target) throw new NotFoundException('Empresa no encontrada');

    // Validar que el caller es holding
    const caller = await this.prisma.empresas.findUnique({ where: { id: callerEmpresaId } });
    if (!caller || caller.type !== 'holding') {
      throw new BadRequestException('Solo el holding puede eliminar empresas');
    }

    // No puede eliminarse a sí mismo
    if (targetId === callerEmpresaId) {
      throw new BadRequestException('No puedes eliminar tu propia empresa');
    }

    // Validar que target pertenece a la jerarquía del holding (cualquier nivel)
    const esDeJerarquia = await this.prisma.$queryRaw<{ exists: boolean }[]>`
      WITH RECURSIVE jerarquia AS (
        SELECT id, parent_id FROM empresas WHERE id = ${targetId}::uuid
        UNION ALL
        SELECT e.id, e.parent_id FROM empresas e JOIN jerarquia j ON e.id = j.parent_id
      )
      SELECT EXISTS (SELECT 1 FROM jerarquia WHERE id = ${callerEmpresaId}::uuid) AS exists
    `;
    if (!esDeJerarquia[0]?.exists) {
      throw new BadRequestException('Solo puedes eliminar empresas que pertenecen a tu holding');
    }

    this.logger.log(`Eliminación forzada iniciada: ${target.razon_social} (${targetId}) por empresa ${callerEmpresaId}`, 'EmpresasService');

    // Verificar qué tablas opcionales existen en esta BD
    // Tablas que NO manejamos manualmente (limpiar al final dinámicamente)
    const manuallyHandled = new Set([
      'empresas', 'suscripciones', 'suscripcion_modulos', 'suscripcion_modulos_historial',
      'suscripcion_pagos', 'comprobante_pago_recibidos', 'comisiones_nuevas',
      'lote_cab', 'log_processing', 'eventos_cab', 'documentos_electronicos',
      'factura_cab', 'factura_det', 'factura_cuotas', 'factura_forma_pagos',
      'factura_subtotales', 'nota_credito_cab', 'nota_credito_det', 'nota_credito_subtotal',
      'asignacion_facturas', 'comisiones', 'cuotas_calculadas', 'oferta_usos',
      'promesas_pago', 'autorizaciones_descuento', 'nota_remision_cab',
      'nota_remision_det', 'nota_remision_factura', 'compra_asistida', 'compra_asistida_items',
      'pedidos', 'solicitud_credito', 'cuentas_cobrar', 'detalle_pago',
      'clientes', 'personas', 'productos', 'condiciones_pago', 'empresa_monedas',
      'empresa_medio_pago', 'pago_bancard', 'ai_empresa_config', 'autofacturas',
      'arqueo_cajas', 'recibos_cobro', 'orden_pago_proveedor_cab', 'orden_pago_proveedor_det',
      'pagos_proveedor', 'movimiento_cajas', 'sesiones_caja', 'audit_logs',
      'usuario', 'usuario_perfiles', 'usuario_empresas', 'codigos_verificacion',
      'usuario_funcional', 'imp_bitacora', 'imp_conceptos', 'imp_componentes',
      'imp_items', 'imp_embarques', 'imp_isc', 'imp_despachos',
    ]);

    // Obtener todas las tablas con FK directa a empresas.id (catálogo nativo PG)
    const fkTablesResult = await this.prisma.$queryRaw<{ table_name: string; column_name: string }[]>`
      SELECT cl.relname AS table_name, att.attname AS column_name
      FROM pg_constraint c
      JOIN pg_class cl  ON c.conrelid  = cl.oid
      JOIN pg_class cl2 ON c.confrelid = cl2.oid
      JOIN pg_attribute att ON att.attrelid = cl.oid AND att.attnum = c.conkey[1]
      WHERE c.contype = 'f' AND cl2.relname = 'empresas'
    `;

    // has() = la tabla existe en la BD y tiene FK a empresas
    const existingFkTables = new Set(fkTablesResult.map((r) => r.table_name));
    const has = (t: string) => existingFkTables.has(t);

    // Tablas con FK directa a empresas que no manejamos manualmente (limpiar dinámicamente)
    const dynamicTables = fkTablesResult.filter((r) => !manuallyHandled.has(r.table_name));

    // Cascade delete en orden de dependencias FK
    const e = `'${targetId}'`; // UUID validado de la BD — seguro para interpolación
    await this.prisma.$transaction(
      async (tx) => {
        const d = (sql: string) => tx.$executeRawUnsafe(sql);

        // ── 1. Suscripciones ──────────────────────────────────────────
        await d(`DELETE FROM suscripcion_modulos_historial WHERE suscripcion_modulo_id IN (SELECT id FROM suscripcion_modulos WHERE suscripcion_id IN (SELECT id FROM suscripciones WHERE empresa_id = ${e}::uuid))`);
        await d(`DELETE FROM suscripcion_modulos WHERE suscripcion_id IN (SELECT id FROM suscripciones WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM suscripcion_pagos WHERE suscripcion_id IN (SELECT id FROM suscripciones WHERE empresa_id = ${e}::uuid)`);
        if (has('comprobante_pago_recibidos')) await d(`DELETE FROM comprobante_pago_recibidos WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM comisiones_nuevas WHERE suscripcion_id IN (SELECT id FROM suscripciones WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM suscripciones WHERE empresa_id = ${e}::uuid`);

        // ── 2. Lotes ──────────────────────────────────────────────────
        await d(`DELETE FROM log_processing WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM eventos_cab WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM documentos_electronicos WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM lote_cab WHERE empresa_id = ${e}::uuid`);

        // ── 3. Facturas y sus hijos ───────────────────────────────────
        await d(`DELETE FROM nota_credito_det WHERE nota_credito_cab_id IN (SELECT id FROM nota_credito_cab WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM nota_credito_subtotal WHERE nota_credito_cab_id IN (SELECT id FROM nota_credito_cab WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM nota_credito_cab WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM factura_subtotales WHERE factura_cab_id IN (SELECT id FROM factura_cab WHERE empresa_id = ${e}::uuid)`);
        if (has('asignacion_facturas')) await d(`DELETE FROM asignacion_facturas WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM comisiones WHERE empresa_id = ${e}::uuid`);
        if (has('cuotas_calculadas')) await d(`DELETE FROM cuotas_calculadas WHERE factura_id IN (SELECT id FROM factura_cab WHERE empresa_id = ${e}::uuid)`);
        if (has('oferta_usos')) await d(`DELETE FROM oferta_usos WHERE factura_id IN (SELECT id FROM factura_cab WHERE empresa_id = ${e}::uuid)`);
        if (has('promesas_pago')) await d(`DELETE FROM promesas_pago WHERE empresa_id = ${e}::uuid`);
        if (has('autorizaciones_descuento')) await d(`DELETE FROM autorizaciones_descuento WHERE empresa_id = ${e}::uuid`);
        if (has('nota_remision_cab')) {
          await d(`DELETE FROM nota_remision_det WHERE nota_remision_cab_id IN (SELECT id FROM nota_remision_cab WHERE empresa_id = ${e}::uuid)`);
          await d(`DELETE FROM nota_remision_factura WHERE nota_remision_cab_id IN (SELECT id FROM nota_remision_cab WHERE empresa_id = ${e}::uuid)`);
          await d(`DELETE FROM nota_remision_cab WHERE empresa_id = ${e}::uuid`);
        }
        if (has('compra_asistida')) {
          if (has('compra_asistida_items')) await d(`DELETE FROM compra_asistida_items WHERE compra_asistida_id IN (SELECT id FROM compra_asistida WHERE empresa_id = ${e}::uuid)`);
          await d(`DELETE FROM compra_asistida WHERE empresa_id = ${e}::uuid`);
        }
        if (has('pedidos')) await d(`UPDATE pedidos SET factura_id = NULL WHERE empresa_id = ${e}::uuid`);
        if (has('solicitud_credito')) await d(`UPDATE solicitud_credito SET factura_id = NULL WHERE empresa_id = ${e}::uuid`);
        // factura_det_lote cascades from factura_det
        await d(`DELETE FROM detalle_pago WHERE factura_cuota_id IN (SELECT id FROM factura_cuotas WHERE factura_cab_id IN (SELECT id FROM factura_cab WHERE empresa_id = ${e}::uuid))`);
        await d(`DELETE FROM factura_forma_pagos WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM factura_cuotas WHERE factura_cab_id IN (SELECT id FROM factura_cab WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM cuentas_cobrar WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM factura_det WHERE factura_cab_id IN (SELECT id FROM factura_cab WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM factura_cab WHERE empresa_id = ${e}::uuid`);
        if (has('pedidos')) await d(`DELETE FROM pedidos WHERE empresa_id = ${e}::uuid`);
        if (has('solicitud_credito')) await d(`DELETE FROM solicitud_credito WHERE empresa_id = ${e}::uuid`);

        // ── 4. Importaciones (opcionales) ─────────────────────────────
        if (has('imp_bitacora')) await d(`DELETE FROM imp_bitacora WHERE empresa_id = ${e}::uuid`);
        if (has('imp_conceptos')) await d(`DELETE FROM imp_conceptos WHERE empresa_id = ${e}::uuid`);
        if (has('imp_componentes')) await d(`DELETE FROM imp_componentes WHERE empresa_id = ${e}::uuid`);
        if (has('imp_items')) await d(`DELETE FROM imp_items WHERE empresa_id = ${e}::uuid`);
        if (has('imp_embarques')) await d(`DELETE FROM imp_embarques WHERE empresa_id = ${e}::uuid`);
        if (has('imp_isc')) await d(`DELETE FROM imp_isc WHERE empresa_id = ${e}::uuid`);
        if (has('imp_despachos')) await d(`DELETE FROM imp_despachos WHERE empresa_id = ${e}::uuid`);

        // ── 5. Clientes y personas ────────────────────────────────────
        await d(`DELETE FROM clientes WHERE persona_id IN (SELECT id FROM personas WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM personas WHERE empresa_id = ${e}::uuid`);

        // ── 6. Productos y configuración ──────────────────────────────
        await d(`DELETE FROM productos WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM condiciones_pago WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM empresa_monedas WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM empresa_medio_pago WHERE empresa_id = ${e}::uuid`);
        if (has('pago_bancard')) await d(`DELETE FROM pago_bancard WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM ai_empresa_config WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM autofacturas WHERE empresa_id = ${e}::uuid`);

        // ── 7. Caja / sesiones ────────────────────────────────────────
        await d(`DELETE FROM arqueo_cajas WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM recibos_cobro WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM orden_pago_proveedor_det WHERE orden_pago_id IN (SELECT id FROM orden_pago_proveedor_cab WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM orden_pago_proveedor_cab WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM pagos_proveedor WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM movimiento_cajas WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM sesiones_caja WHERE empresa_id = ${e}::uuid`);

        // ── 8. Auditoría ──────────────────────────────────────────────
        await d(`DELETE FROM audit_logs WHERE empresa_id = ${e}::uuid`);

        // ── 9. Usuarios ───────────────────────────────────────────────
        await d(`DELETE FROM usuario_perfiles WHERE user_id IN (SELECT id FROM usuario WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM usuario_empresas WHERE usuario_id IN (SELECT id FROM usuario WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM codigos_verificacion WHERE usuario_id IN (SELECT id FROM usuario WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM suscripcion_modulos_historial WHERE usuario_id IN (SELECT id FROM usuario WHERE empresa_id = ${e}::uuid)`);
        await d(`DELETE FROM usuario WHERE empresa_id = ${e}::uuid`);
        await d(`DELETE FROM usuario_funcional WHERE empresa = ${e}::uuid`);

        // ── 10. Todas las tablas restantes con FK directa a empresas (dinámico) ───
        for (const { table_name, column_name } of dynamicTables) {
          await d(`DELETE FROM "${table_name}" WHERE "${column_name}" = ${e}::uuid`);
        }

        // ── 11. Empresa ────────────────────────────────────────────────
        await d(`DELETE FROM empresas WHERE id = ${e}::uuid`);
      },
      { timeout: 30000 },
    );

    this.logger.log(`Empresa eliminada permanentemente: ${target.razon_social} (${targetId})`, 'EmpresasService');
    return { message: `Empresa "${target.razon_social}" eliminada permanentemente`, razon_social: target.razon_social };
  }

  async uploadLogo(empresaId: string, logoUrl: string, user_id?: string) {
    await this.findOne(empresaId);

    const empresa = await this.prisma.empresas.update({
      where: { id: empresaId },
      data: {
        logo: logoUrl,
        updated_at: new Date(),
      },
    });

    this.logger.log(`Logo actualizado para empresa (${empresaId})`, 'EmpresasService');
    await this.auditService.log({
      empresa_id: empresaId,
      user_id,
      action: 'UPDATE',
      entity_type: 'empresa',
      entity_id: empresaId,
      descripcion: `Logo actualizado`,
      new_value: { logo: logoUrl },
    });
    return { logo: empresa.logo };
  }

  // ─── Empresas Correo CRUD ────────────────────────────────────────────

  async getEmpresaCorreo(empresaId: string) {
    const empresa = await this.prisma.empresas.findUnique({
      where: { id: empresaId },
      select: { usar_correo_propio: true },
    });

    // Correo propio de la empresa (no system)
    const correoPropio = await this.prisma.empresas_correo.findFirst({
      where: { empresa_id: empresaId, system: { not: true } },
    });

    // Correo system (el del sistema)
    const correoSystem = await this.prisma.empresas_correo.findFirst({
      where: { system: true },
    });

    return {
      usar_correo_propio: empresa?.usar_correo_propio ?? false,
      correo_propio: correoPropio || null,
      correo_system: correoSystem ? { email: correoSystem.email, id: correoSystem.id } : null,
    };
  }

  /**
   * Retorna las credenciales efectivas de correo para una empresa.
   * Si usa correo propio, retorna su config. Si no, retorna el system.
   */
  async getCorreoEfectivo(empresaId: string) {
    const empresa = await this.prisma.empresas.findUnique({
      where: { id: empresaId },
      select: { usar_correo_propio: true },
    });

    if (empresa?.usar_correo_propio) {
      const propio = await this.prisma.empresas_correo.findFirst({
        where: { empresa_id: empresaId, system: { not: true } },
      });
      if (propio) return propio;
    }

    // Fallback: correo system
    const system = await this.prisma.empresas_correo.findFirst({
      where: { system: true },
    });
    return system;
  }

  async upsertEmpresaCorreo(empresaId: string, dto: UpsertEmpresaCorreoDto, userId?: string) {
    // 1. Actualizar flag usar_correo_propio en la empresa
    await this.prisma.empresas.update({
      where: { id: empresaId },
      data: { usar_correo_propio: dto.usar_correo_propio, updated_at: new Date() },
    });

    let result = null;

    // 2. Si usa correo propio, guardar/actualizar credenciales SMTP
    if (dto.usar_correo_propio) {
      if (!dto.host || !dto.username || !dto.password || !dto.email) {
        throw new BadRequestException('Debe completar todos los campos SMTP para usar correo propio');
      }

      const existing = await this.prisma.empresas_correo.findFirst({
        where: { empresa_id: empresaId, system: { not: true } },
      });

      const data = {
        host: dto.host,
        puerto: dto.puerto || '465',
        username: dto.username,
        password: dto.password,
        email: dto.email,
        envio_ssl: dto.envio_ssl ?? true,
        template_personalizado: dto.template_personalizado ?? false,
        adjunto: dto.adjunto ?? true,
        system: false,
        usuario: 'admin',
        updated_at: new Date(),
      };

      if (existing) {
        result = await this.prisma.empresas_correo.update({
          where: { id: existing.id },
          data,
        });
      } else {
        result = await this.prisma.empresas_correo.create({
          data: {
            ...data,
            empresa_id: empresaId,
            created_at: new Date(),
          },
        });
      }
    }

    await this.auditService.log({
      empresa_id: empresaId,
      user_id: userId,
      action: 'UPDATE',
      entity_type: 'empresas_correo',
      entity_id: result?.id || empresaId,
      descripcion: dto.usar_correo_propio ? 'Correo propio configurado' : 'Empresa cambiada a correo del sistema',
      new_value: { usar_correo_propio: dto.usar_correo_propio, email: dto.email },
    });

    // Sincronizar con middleware (envía las credenciales efectivas)
    try {
      await this.queuesService.enqueueMiddlewareEmpresa(empresaId, 'M', 'empresas_correo');
    } catch (e) {
      this.logger.warn(`No se pudo encolar sync correo middleware: ${e}`, 'EmpresasService');
    }

    return { usar_correo_propio: dto.usar_correo_propio, correo: result };
  }

  async deleteEmpresaCorreo(empresaId: string, userId?: string) {
    const existing = await this.prisma.empresas_correo.findFirst({
      where: { empresa_id: empresaId, system: { not: true } },
    });
    if (!existing) throw new NotFoundException('Configuración de correo propio no encontrada');

    await this.prisma.empresas_correo.delete({ where: { id: existing.id } });

    // Volver a usar correo del sistema
    await this.prisma.empresas.update({
      where: { id: empresaId },
      data: { usar_correo_propio: false, updated_at: new Date() },
    });

    await this.auditService.log({
      empresa_id: empresaId,
      user_id: userId,
      action: 'DELETE',
      entity_type: 'empresas_correo',
      entity_id: existing.id,
      descripcion: 'Correo propio eliminado, vuelve a correo del sistema',
    });

    // Sync middleware con correo system
    try {
      await this.queuesService.enqueueMiddlewareEmpresa(empresaId, 'M', 'empresas_correo');
    } catch (e) {
      this.logger.warn(`No se pudo encolar sync correo middleware: ${e}`, 'EmpresasService');
    }

    return { message: 'Correo propio eliminado' };
  }

  async testEmpresaCorreo(empresaId: string, destinatario: string) {
    const correo = await this.getCorreoEfectivo(empresaId);
    if (!correo) throw new NotFoundException('No hay configuración de correo disponible. Contacte al administrador.');

    const empresa = await this.prisma.empresas.findUnique({
      where: { id: empresaId },
      select: { razon_social: true },
    });

    const port = Number(correo.puerto) || 465;
    const useImplicitSSL = port === 465;

    const transporter = nodemailer.createTransport({
      host: correo.host,
      port,
      secure: useImplicitSSL,
      auth: {
        user: correo.username,
        pass: correo.password,
      },
      ...(!useImplicitSSL && correo.envio_ssl ? { requireTLS: true } : {}),
      tls: { rejectUnauthorized: false },
    });

    try {
      await transporter.sendMail({
        from: `"${empresa?.razon_social || 'Novasis PY'}" <${correo.email}>`,
        to: destinatario,
        subject: 'Correo de prueba - Novasis PY',
        html: `
          <div style="font-family:Arial,sans-serif;max-width:500px;margin:0 auto;padding:20px">
            <h2 style="color:#1976d2">Correo de prueba</h2>
            <p>Este es un correo de prueba enviado desde <strong>${empresa?.razon_social || 'Novasis PY'}</strong>.</p>
            <p>Si recibiste este correo, la configuración SMTP es correcta.</p>
            <hr style="border:none;border-top:1px solid #e0e0e0;margin:20px 0">
            <p style="color:#999;font-size:12px">Enviado desde Novasis PY - Sistema de Facturación Electrónica</p>
          </div>
        `,
      });
    } catch (err) {
      const raw = err instanceof Error ? err.message : String(err);
      throw new BadRequestException(this.parseSmtpError(raw, correo.host, port));
    }

    return { message: 'Correo de prueba enviado exitosamente' };
  }

  private parseSmtpError(raw: string, host: string, port: number): string {
    const lower = raw.toLowerCase();

    if (lower.includes('wrong version number') || lower.includes('ssl routines'))
      return `Error SSL/TLS: El puerto ${port} no soporta el tipo de conexión configurado. Probá con puerto 587 (STARTTLS) o 465 (SSL directo).`;

    if (lower.includes('econnrefused'))
      return `No se pudo conectar al servidor ${host}:${port}. Verificá que el host y puerto sean correctos.`;

    if (lower.includes('etimedout') || lower.includes('timeout'))
      return `Tiempo de espera agotado conectando a ${host}:${port}. El servidor no responde o el puerto está bloqueado.`;

    if (lower.includes('enotfound') || lower.includes('getaddrinfo'))
      return `No se encontró el servidor "${host}". Verificá que el nombre del host SMTP sea correcto.`;

    if (lower.includes('invalid login') || lower.includes('authentication') || lower.includes('535'))
      return 'Usuario o contraseña SMTP incorrectos. Verificá las credenciales de acceso.';

    if (lower.includes('self signed') || lower.includes('certificate'))
      return `El certificado SSL del servidor ${host} no es válido o es auto-firmado.`;

    if (lower.includes('greeting') || lower.includes('connection closed'))
      return `El servidor ${host}:${port} cerró la conexión inesperadamente. Verificá puerto y configuración SSL.`;

    if (lower.includes('envelope') || lower.includes('sender') || lower.includes('from'))
      return 'El email remitente no es aceptado por el servidor. Verificá que el email coincida con la cuenta SMTP.';

    return `Error SMTP: ${raw}`;
  }
}
