import { BadRequestException, ConflictException, Injectable, NotFoundException, Logger } from '@nestjs/common';
import { AuditService } from 'src/audit/audit.service';
import { PersonasService } from 'src/personas/personas.service';
import { PrismaService } from 'src/prisma/prisma.service';
import { whereProveedorPorRubro } from 'src/common/utils/rubros.util';
import { rubrosFiltroUsuario } from 'src/utils/alcance-rubro';
import { CreateProveedorDto, TIPOS_ENTIDAD_PROVEEDOR, TipoEntidadProveedor } from './dto/create-proveedor.dto';
import { UpdateProveedorDto } from './dto/update-proveedor.dto';

@Injectable()
export class ProveedoresService {
  private readonly logger = new Logger(ProveedoresService.name);
  constructor(
    private readonly prisma: PrismaService,
    private readonly personasService: PersonasService,
    private readonly auditService: AuditService,
  ) {}

  /**
   * Normaliza el par (tipo_entidad, es_extranjero) y valida cuenta_contable_id.
   * Ver docs/plan-acreedores-proveedores-exterior.md — regla de sincronía.
   *
   * Reglas:
   *  - Si viene `tipo_entidad`, gana → deriva `es_extranjero` (true ⇔ PROVEEDOR_EXTERIOR).
   *  - Si solo viene `es_extranjero`, se traduce a tipo_entidad legacy.
   *  - Si viene `cuenta_contable_id`, se verifica que la cuenta exista en la empresa,
   *    sea de tipo PASIVO acreedor y `acepta_movimientos=true`.
   */
  private async resolverFaseAcreedoresPayload(
    dto: Partial<CreateProveedorDto>,
    empresa_id: string,
    fallback?: { tipo_entidad?: string | null; es_extranjero?: boolean | null; cuenta_contable_id?: string | null },
  ): Promise<{ tipo_entidad: TipoEntidadProveedor; es_extranjero: boolean; cuenta_contable_id: string | null }> {
    // Determinar tipo_entidad efectivo
    let tipo: TipoEntidadProveedor;
    if (dto.tipo_entidad) {
      tipo = dto.tipo_entidad;
    } else if (dto.es_extranjero != null) {
      tipo = dto.es_extranjero ? 'PROVEEDOR_EXTERIOR' : 'PROVEEDOR_LOCAL';
    } else if (fallback?.tipo_entidad && (TIPOS_ENTIDAD_PROVEEDOR as readonly string[]).includes(fallback.tipo_entidad)) {
      tipo = fallback.tipo_entidad as TipoEntidadProveedor;
    } else if (fallback?.es_extranjero != null) {
      tipo = fallback.es_extranjero ? 'PROVEEDOR_EXTERIOR' : 'PROVEEDOR_LOCAL';
    } else {
      tipo = 'PROVEEDOR_LOCAL';
    }

    const esExtranjero = tipo === 'PROVEEDOR_EXTERIOR';

    // Validar override de cuenta contable
    let cuentaId = dto.cuenta_contable_id ?? fallback?.cuenta_contable_id ?? null;
    if (dto.cuenta_contable_id === null) cuentaId = null; // permitir limpiar explícitamente
    if (cuentaId) {
      const cuenta = await this.prisma.cont_plan_cuentas.findFirst({
        where: {
          id: cuentaId,
          active: true,
          OR: [{ empresa_id }, { empresa_id: null }],
        },
        select: { id: true, tipo: true, naturaleza: true, acepta_movimientos: true },
      });
      if (!cuenta) {
        throw new BadRequestException('La cuenta contable seleccionada no existe o no pertenece a la empresa.');
      }
      if (!cuenta.acepta_movimientos) {
        throw new BadRequestException(
          'La cuenta contable seleccionada es de agrupación (no acepta movimientos). Elegí una cuenta hoja.',
        );
      }
      if (cuenta.tipo !== 'PASIVO' || cuenta.naturaleza !== 'ACREEDORA') {
        throw new BadRequestException(
          'La cuenta contable del proveedor debe ser de tipo PASIVO con naturaleza ACREEDORA.',
        );
      }
    }

    return { tipo_entidad: tipo, es_extranjero: esExtranjero, cuenta_contable_id: cuentaId };
  }

  async create(dto: CreateProveedorDto, empresa_id: string, user_id?: string) {
    try {
      const persona = await this.personasService.create(dto, empresa_id);

      const proveedorExistente = await this.prisma.proveedores.findFirst({
        where: {
          empresa_id,
          persona_id: persona.id,
        },
      });

      if (proveedorExistente?.activo) {
        throw new ConflictException('El proveedor ya existe');
      }

      // FASE 1 — Acreedores y proveedores del exterior: normalizar tipo/legacy y validar override
      const face = await this.resolverFaseAcreedoresPayload(
        dto,
        empresa_id,
        proveedorExistente
          ? {
              tipo_entidad: (proveedorExistente as any).tipo_entidad ?? null,
              es_extranjero: proveedorExistente.es_extranjero ?? null,
              cuenta_contable_id: (proveedorExistente as any).cuenta_contable_id ?? null,
            }
          : undefined,
      );

      const vencTimbrado = dto.fecha_vencimiento_timbrado ? new Date(dto.fecha_vencimiento_timbrado) : null;
      const proveedor = proveedorExistente
        ? await this.prisma.proveedores.update({
            where: { id: proveedorExistente.id },
            data: {
              ciudad_id: dto.ciudad_id ?? persona.ciudad_id ?? null,
              observaciones: dto.observaciones ?? null,
              activo: true,
              tipo_entidad: face.tipo_entidad,
              es_extranjero: face.es_extranjero,
              cuenta_contable_id: face.cuenta_contable_id,
              pais: dto.pais ?? null,
              swift: dto.swift ?? null,
              banco_corresponsal: dto.banco_corresponsal ?? null,
              timbrado: dto.timbrado ?? null,
              fecha_vencimiento_timbrado: vencTimbrado,
              updated_at: new Date(),
            },
          })
        : await this.prisma.proveedores.create({
            data: {
              empresa_id,
              persona_id: persona.id,
              ciudad_id: dto.ciudad_id ?? persona.ciudad_id ?? null,
              observaciones: dto.observaciones ?? null,
              activo: true,
              tipo_entidad: face.tipo_entidad,
              es_extranjero: face.es_extranjero,
              cuenta_contable_id: face.cuenta_contable_id,
              pais: dto.pais ?? null,
              swift: dto.swift ?? null,
              banco_corresponsal: dto.banco_corresponsal ?? null,
              timbrado: dto.timbrado ?? null,
              fecha_vencimiento_timbrado: vencTimbrado,
            },
          });

      this.logger.log(`Proveedor creado: ${persona.razon_social} (${proveedor.id})`, 'ProveedoresService');
      await this.auditService.log({
        empresa_id,
        user_id,
        action: 'CREATE',
        entity_type: 'proveedor',
        entity_id: proveedor.id,
        descripcion: `Proveedor creado: ${persona.razon_social}`,
        new_value: {
          razon_social: persona.razon_social,
          ruc: persona.ruc,
          observaciones: proveedor.observaciones,
        },
      });

      return {
        message: 'Proveedor creado exitosamente',
        data: { persona, proveedor },
      };
    } catch (error) {
      if (error instanceof ConflictException) throw error;
      if (error instanceof NotFoundException) throw error;
      if (error instanceof BadRequestException) throw error;

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

  async search(
    query: string,
    empresa_id: string,
    limit = 20,
    opts?: {
      tipo_entidad?: TipoEntidadProveedor | TipoEntidadProveedor[];
      rubro_id?: string | null;
      /** Para validar el rubro contra los permitidos del usuario. */
      usuario_id?: string;
    },
  ) {
    const tipoFiltro = opts?.tipo_entidad
      ? Array.isArray(opts.tipo_entidad)
        ? { in: opts.tipo_entidad }
        : opts.tipo_entidad
      : undefined;

    // Con query >= 2 chars filtra por nombre/RUC/documento; sin query devuelve todos activos.
    const textFilter =
      query && query.trim().length >= 2
        ? {
            OR: [
              { personas: { razon_social: { contains: query.trim(), mode: 'insensitive' as const } } },
              { personas: { ruc: { contains: query.trim(), mode: 'insensitive' as const } } },
              { personas: { nro_documento: { contains: query.trim(), mode: 'insensitive' as const } } },
            ],
          }
        : {};

    const proveedores = await this.prisma.proveedores.findMany({
      where: {
        empresa_id,
        activo: true,
        ...(tipoFiltro ? { tipo_entidad: tipoFiltro as any } : {}),
        ...textFilter,
        ...whereProveedorPorRubro(await rubrosFiltroUsuario(this.prisma, opts?.usuario_id, empresa_id, opts?.rubro_id)),
      },
      select: {
        id: true,
        observaciones: true,
        activo: true,
        tipo_entidad: true,
        es_extranjero: true,
        cuenta_contable_id: true,
        pais: true,
        swift: true,
        banco_corresponsal: true,
        timbrado: true,
        fecha_vencimiento_timbrado: true,
        created_at: true,
        updated_at: true,
        personas: {
          select: {
            id: true,
            razon_social: true,
            ruc: true,
            dv: true,
            nro_documento: true,
            direccion: true,
            telefono: true,
            celular: true,
            email: true,
            paises: true,
          },
        },
      },
      orderBy: { personas: { razon_social: 'asc' } },
      take: Math.max(1, Math.min(100, Number(limit) || 20)),
    });

    return proveedores;
  }

  async findAll(
    page = 1,
    limit = 10,
    empresa_id: string,
    q?: string,
    opts?: {
      tipo_entidad?: TipoEntidadProveedor | TipoEntidadProveedor[];
      rubro_id?: string | null;
      /** Para validar el rubro contra los permitidos del usuario. */
      usuario_id?: string;
    },
  ) {
    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 tipoFiltro = opts?.tipo_entidad
      ? Array.isArray(opts.tipo_entidad)
        ? { in: opts.tipo_entidad }
        : opts.tipo_entidad
      : undefined;

    const textFilter =
      q && q.trim().length >= 2
        ? {
            OR: [
              { personas: { razon_social: { contains: q.trim(), mode: 'insensitive' as const } } },
              { personas: { ruc: { contains: q.trim(), mode: 'insensitive' as const } } },
              { personas: { nro_documento: { contains: q.trim(), mode: 'insensitive' as const } } },
            ],
          }
        : {};

    const where = {
      empresa_id,
      activo: true,
      ...(tipoFiltro ? { tipo_entidad: tipoFiltro as any } : {}),
      ...textFilter,
      ...whereProveedorPorRubro(await rubrosFiltroUsuario(this.prisma, opts?.usuario_id, empresa_id, opts?.rubro_id)),
    };

    const [items, total] = await this.prisma.$transaction([
      this.prisma.proveedores.findMany({
        where,
        select: {
          id: true,
          empresa_id: true,
          persona_id: true,
          ciudad_id: true,
          observaciones: true,
          activo: true,
          tipo_entidad: true,
          es_extranjero: true,
          cuenta_contable_id: true,
          pais: true,
          swift: true,
          banco_corresponsal: true,
          timbrado: true,
          fecha_vencimiento_timbrado: true,
          created_at: true,
          updated_at: true,
          // Rubros asignados, para el chip del listado. Array vacío = todos.
          proveedor_rubros: {
            select: { rubros: { select: { id: true, codigo: true, descripcion: true, color: true, icono: true } } },
          },
          personas: {
            select: {
              id: true,
              razon_social: true,
              ruc: true,
              dv: true,
              naturaleza_receptor: true,
              tipo_contribuyente: true,
              tipo_documento_identidad: true,
              nro_documento: true,
              direccion: true,
              nro_casa: true,
              telefono: true,
              celular: true,
              email: true,
              pais_id: true,
              ciudad_id: true,
              paises: {
                select: {
                  id: true,
                  codigo: true,
                  descripcion: true,
                },
              },
              ciudades: {
                select: {
                  id: true,
                  codigo: true,
                  descripcion: true,
                },
              },
            },
          },
          ciudades: {
            select: {
              id: true,
              codigo: true,
              descripcion: true,
            },
          },
        },
        skip,
        take,
        orderBy: { created_at: 'desc' },
      }),
      this.prisma.proveedores.count({ where }),
    ]);

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

  async findOne(id: string, empresa_id: string) {
    const proveedor = await this.prisma.proveedores.findFirst({
      where: { id, empresa_id },
      select: {
        id: true,
        empresa_id: true,
        persona_id: true,
        ciudad_id: true,
        observaciones: true,
        activo: true,
        tipo_entidad: true,
        es_extranjero: true,
        cuenta_contable_id: true,
        pais: true,
        swift: true,
        banco_corresponsal: true,
        timbrado: true,
        fecha_vencimiento_timbrado: true,
        created_at: true,
        updated_at: true,
        personas: {
          select: {
            id: true,
            razon_social: true,
            ruc: true,
            dv: true,
            naturaleza_receptor: true,
            tipo_contribuyente: true,
            tipo_documento_identidad: true,
            nro_documento: true,
            otro_tipo_documento: true,
            direccion: true,
            nro_casa: true,
            telefono: true,
            celular: true,
            email: true,
            pais_id: true,
            ciudad_id: true,
            paises: {
              select: { id: true, codigo: true, descripcion: true },
            },
            ciudades: {
              select: { id: true, codigo: true, descripcion: true },
            },
          },
        },
        ciudades: {
          select: { id: true, codigo: true, descripcion: true },
        },
      },
    });

    if (!proveedor) throw new NotFoundException('Proveedor no encontrado');
    return { message: 'Proveedor encontrado exitosamente', data: proveedor };
  }

  async update(id: string, dto: UpdateProveedorDto, empresa_id: string, user_id?: string) {
    try {
      const proveedorFind = await this.prisma.proveedores.findFirst({
        where: { id, empresa_id },
      });
      if (!proveedorFind) throw new NotFoundException('Proveedor no encontrado');
      if (!proveedorFind.activo) throw new ConflictException('El proveedor se encuentra inactivo');

      let personaId = proveedorFind.persona_id ?? null;
      if (personaId) {
        await this.personasService.update(personaId, dto, empresa_id);
      } else {
        if (!dto.naturaleza_id || !dto.razon_social) {
          throw new BadRequestException(
            'Para crear persona en actualización de proveedor se requiere naturaleza_id y razon_social',
          );
        }

        const createPersonaDto: CreateProveedorDto = {
          ...dto,
          naturaleza_id: dto.naturaleza_id,
          razon_social: dto.razon_social,
        };
        const nuevaPersona = await this.personasService.create(createPersonaDto, empresa_id);
        personaId = nuevaPersona.id;
      }

      // FASE 1 — normalizar tipo/legacy y validar override
      const face = await this.resolverFaseAcreedoresPayload(dto, empresa_id, {
        tipo_entidad: (proveedorFind as any).tipo_entidad ?? null,
        es_extranjero: proveedorFind.es_extranjero ?? null,
        cuenta_contable_id: (proveedorFind as any).cuenta_contable_id ?? null,
      });

      const proveedor = await this.prisma.proveedores.update({
        where: { id },
        data: {
          persona_id: personaId,
          ciudad_id: dto.ciudad_id ?? proveedorFind.ciudad_id ?? null,
          observaciones: dto.observaciones ?? proveedorFind.observaciones ?? null,
          tipo_entidad: face.tipo_entidad,
          es_extranjero: face.es_extranjero,
          cuenta_contable_id: face.cuenta_contable_id,
          ...(dto.pais !== undefined && { pais: dto.pais }),
          ...(dto.swift !== undefined && { swift: dto.swift }),
          ...(dto.banco_corresponsal !== undefined && { banco_corresponsal: dto.banco_corresponsal }),
          ...(dto.timbrado !== undefined && { timbrado: dto.timbrado || null }),
          ...(dto.fecha_vencimiento_timbrado !== undefined && {
            fecha_vencimiento_timbrado: dto.fecha_vencimiento_timbrado ? new Date(dto.fecha_vencimiento_timbrado) : null,
          }),
          updated_at: new Date(),
        },
      });

      this.logger.log(`Proveedor actualizado: (${id})`, 'ProveedoresService');
      await this.auditService.log({
        empresa_id,
        user_id,
        action: 'UPDATE',
        entity_type: 'proveedor',
        entity_id: id,
        descripcion: 'Proveedor actualizado',
        old_value: {
          ciudad_id: proveedorFind.ciudad_id,
          observaciones: proveedorFind.observaciones,
          activo: proveedorFind.activo,
          tipo_entidad: (proveedorFind as any).tipo_entidad ?? null,
          es_extranjero: proveedorFind.es_extranjero,
          cuenta_contable_id: (proveedorFind as any).cuenta_contable_id ?? null,
        },
        new_value: {
          ciudad_id: proveedor.ciudad_id,
          observaciones: proveedor.observaciones,
          activo: proveedor.activo,
          tipo_entidad: proveedor.tipo_entidad,
          es_extranjero: proveedor.es_extranjero,
          cuenta_contable_id: proveedor.cuenta_contable_id,
        },
      });

      return { message: 'Proveedor actualizado exitosamente', data: proveedor };
    } catch (error) {
      if (error instanceof NotFoundException) throw error;
      if (error instanceof ConflictException) throw error;
      if (error instanceof BadRequestException) throw error;
      const message = error instanceof Error ? error.message : 'Error desconocido';
      throw new Error(`Error al actualizar proveedor: ${message}`);
    }
  }

  async activarDesactivar(id: string, empresa_id: string, user_id?: string) {
    try {
      const proveedorFind = await this.prisma.proveedores.findFirst({
        where: { id, empresa_id },
      });
      if (!proveedorFind) throw new NotFoundException('Proveedor no encontrado');

      const proveedor = await this.prisma.proveedores.update({
        where: { id },
        data: {
          activo: !proveedorFind.activo,
          updated_at: new Date(),
        },
      });

      const action = proveedor.activo ? 'ACTIVATE' : 'DEACTIVATE';
      this.logger.log(`Proveedor (${id}) ${proveedor.activo ? 'activado' : 'desactivado'}`, 'ProveedoresService');
      await this.auditService.log({
        empresa_id,
        user_id,
        action,
        entity_type: 'proveedor',
        entity_id: id,
        descripcion: `Proveedor ${proveedor.activo ? 'activado' : 'desactivado'}`,
        old_value: { activo: proveedorFind.activo },
        new_value: { activo: proveedor.activo },
      });

      return { message: 'Proveedor actualizado exitosamente', data: proveedor };
    } catch (error) {
      if (error instanceof NotFoundException) throw error;
      const message = error instanceof Error ? error.message : 'Error desconocido';
      throw new Error(`Error al actualizar proveedor: ${message}`);
    }
  }

  /**
   * FASE 3 — Reporte de clasificación de terceros.
   *
   * Devuelve por cada proveedor: tipo_entidad, cuenta contable efectiva
   * (override → concepto → PROVEEDORES) y saldo CxP abierto.
   */
  async reporteClasificacion(
    empresa_id: string,
    opts?: { tipo_entidad?: TipoEntidadProveedor | TipoEntidadProveedor[]; activo?: boolean },
  ) {
    const tipoFiltro = opts?.tipo_entidad
      ? Array.isArray(opts.tipo_entidad)
        ? { in: opts.tipo_entidad }
        : opts.tipo_entidad
      : undefined;

    const proveedores = await this.prisma.proveedores.findMany({
      where: {
        empresa_id,
        ...(opts?.activo != null ? { activo: opts.activo } : {}),
        ...(tipoFiltro ? { tipo_entidad: tipoFiltro as any } : {}),
      },
      select: {
        id: true,
        activo: true,
        tipo_entidad: true,
        es_extranjero: true,
        cuenta_contable_id: true,
        cuenta_contable: {
          select: { id: true, codigo: true, descripcion: true, active: true, acepta_movimientos: true },
        },
        personas: { select: { razon_social: true, ruc: true, dv: true, nro_documento: true } },
      },
      orderBy: { personas: { razon_social: 'asc' } },
    });

    const ids = proveedores.map((p) => p.id);
    const saldos = ids.length
      ? await this.prisma.cuentas_pagar.groupBy({
          by: ['proveedor_id'],
          where: {
            empresa_id,
            proveedor_id: { in: ids },
            activo: true,
            estado: { notIn: ['pagada', 'anulada'] },
          },
          _sum: { saldo_pendiente: true },
          _count: { _all: true },
        })
      : [];
    const saldoMap = new Map(
      saldos.map((s) => [
        s.proveedor_id,
        { saldo: Number(s._sum.saldo_pendiente ?? 0), cxp_abiertas: s._count._all },
      ]),
    );

    const items = proveedores.map((p) => {
      const s = saldoMap.get(p.id) ?? { saldo: 0, cxp_abiertas: 0 };
      const cuentaOverride =
        p.cuenta_contable && p.cuenta_contable.active && p.cuenta_contable.acepta_movimientos
          ? p.cuenta_contable
          : null;
      return {
        id: p.id,
        razon_social: p.personas?.razon_social ?? null,
        ruc: p.personas?.ruc ?? null,
        dv: p.personas?.dv ?? null,
        nro_documento: p.personas?.nro_documento ?? null,
        activo: p.activo,
        tipo_entidad: (p as any).tipo_entidad,
        es_extranjero: p.es_extranjero,
        cuenta_override: cuentaOverride
          ? { id: cuentaOverride.id, codigo: cuentaOverride.codigo, descripcion: cuentaOverride.descripcion }
          : null,
        cuenta_efectiva_origen: cuentaOverride
          ? 'OVERRIDE'
          : (p as any).tipo_entidad === 'ACREEDOR_VARIO'
          ? 'CONCEPTO_ACREEDORES_VARIOS'
          : (p as any).tipo_entidad === 'PROVEEDOR_EXTERIOR'
          ? 'CONCEPTO_PROVEEDORES_EXTERIOR'
          : 'CONCEPTO_PROVEEDORES',
        saldo_cxp: s.saldo,
        cxp_abiertas: s.cxp_abiertas,
      };
    });

    return {
      total: items.length,
      resumen: {
        PROVEEDOR_LOCAL: items.filter((i) => i.tipo_entidad === 'PROVEEDOR_LOCAL').length,
        PROVEEDOR_EXTERIOR: items.filter((i) => i.tipo_entidad === 'PROVEEDOR_EXTERIOR').length,
        ACREEDOR_VARIO: items.filter((i) => i.tipo_entidad === 'ACREEDOR_VARIO').length,
      },
      items,
    };
  }

  /**
   * FASE 3 — Reclasificación asistida de terceros.
   *
   * Cambia `tipo_entidad` (opcionalmente `cuenta_contable_id`) sin tocar datos de persona.
   * Bloquea reclasificar a ACREEDOR_VARIO si el tercero tiene OC o embarques activos:
   * esos flujos no admiten acreedores varios (ver validateCabeceraRefs en compras/OC/importaciones).
   *
   * Los saldos y asientos existentes NO se migran: la cuenta_pasivo_id ya está congelada
   * en cada CxP. Solo documentos NUEVOS usan la cuenta nueva.
   */
  async reclasificarTipoEntidad(
    id: string,
    dto: { tipo_entidad: string; cuenta_contable_id?: string | null; motivo?: string },
    empresa_id: string,
    user_id?: string,
  ) {
    if (!dto?.tipo_entidad || !(TIPOS_ENTIDAD_PROVEEDOR as readonly string[]).includes(dto.tipo_entidad)) {
      throw new BadRequestException('tipo_entidad inválido');
    }
    const nuevoTipo = dto.tipo_entidad as TipoEntidadProveedor;

    const proveedor = await this.prisma.proveedores.findFirst({
      where: { id, empresa_id },
      select: {
        id: true,
        activo: true,
        tipo_entidad: true,
        es_extranjero: true,
        cuenta_contable_id: true,
        personas: { select: { razon_social: true } },
      },
    });
    if (!proveedor) throw new NotFoundException('Proveedor no encontrado');
    if (!proveedor.activo) throw new ConflictException('El proveedor se encuentra inactivo');

    const tipoActual = (proveedor as any).tipo_entidad as TipoEntidadProveedor | null;
    if (tipoActual === nuevoTipo && dto.cuenta_contable_id === undefined) {
      return { message: 'Sin cambios', data: proveedor };
    }

    // Bloqueo: si el destino es ACREEDOR_VARIO, no puede tener OC ni embarques activos.
    if (nuevoTipo === 'ACREEDOR_VARIO') {
      const [ocActivas, embarquesActivos] = await Promise.all([
        this.prisma.orden_compra_cab.count({
          where: {
            empresa_id,
            proveedor_id: id,
            activo: true,
            estado: { notIn: ['completada', 'cancelada'] },
          },
        }),
        this.prisma.imp_embarques.count({
          where: {
            empresa_id,
            proveedor_id: id,
            activo: true,
            estado: { notIn: ['CERRADO', 'CANCELADO'] },
          },
        }),
      ]);
      if (ocActivas > 0 || embarquesActivos > 0) {
        throw new BadRequestException(
          `No se puede reclasificar a Acreedor Vario: el tercero tiene ${ocActivas} orden(es) de compra y ${embarquesActivos} embarque(s) activos.`,
        );
      }
    }

    const face = await this.resolverFaseAcreedoresPayload(
      { tipo_entidad: nuevoTipo, cuenta_contable_id: dto.cuenta_contable_id },
      empresa_id,
      {
        tipo_entidad: tipoActual,
        es_extranjero: proveedor.es_extranjero ?? null,
        cuenta_contable_id: (proveedor as any).cuenta_contable_id ?? null,
      },
    );

    const actualizado = await this.prisma.proveedores.update({
      where: { id },
      data: {
        tipo_entidad: face.tipo_entidad,
        es_extranjero: face.es_extranjero,
        cuenta_contable_id: face.cuenta_contable_id,
        updated_at: new Date(),
      },
    });

    this.logger.log(
      `Proveedor reclasificado ${tipoActual} → ${face.tipo_entidad} (${id})`,
      'ProveedoresService',
    );
    await this.auditService.log({
      empresa_id,
      user_id,
      action: 'UPDATE',
      entity_type: 'proveedor',
      entity_id: id,
      descripcion: `Reclasificación de tercero: ${tipoActual} → ${face.tipo_entidad}${dto.motivo ? ` — ${dto.motivo}` : ''}`,
      old_value: {
        tipo_entidad: tipoActual,
        es_extranjero: proveedor.es_extranjero,
        cuenta_contable_id: (proveedor as any).cuenta_contable_id ?? null,
      },
      new_value: {
        tipo_entidad: face.tipo_entidad,
        es_extranjero: face.es_extranjero,
        cuenta_contable_id: face.cuenta_contable_id,
        motivo: dto.motivo ?? null,
      },
    });

    return {
      message: 'Tercero reclasificado exitosamente',
      warning:
        'Los saldos y asientos existentes permanecen en la cuenta original. Solo documentos nuevos usarán la cuenta destino.',
      data: actualizado,
    };
  }

  async remove(id: string, empresa_id?: string, user_id?: string) {
    try {
      const proveedorFind = await this.prisma.proveedores.findFirst({
        where: { id, empresa_id },
      });
      if (!proveedorFind) throw new NotFoundException('Proveedor no encontrado');
      if (!proveedorFind.activo) throw new ConflictException('El proveedor ya fue eliminado');

      const proveedor = await this.prisma.proveedores.update({
        where: { id },
        data: {
          activo: false,
          updated_at: new Date(),
        },
      });

      this.logger.log(`Proveedor eliminado: (${id})`, 'ProveedoresService');
      await this.auditService.log({
        empresa_id,
        user_id,
        action: 'DELETE',
        entity_type: 'proveedor',
        entity_id: id,
        descripcion: 'Proveedor eliminado (soft delete)',
        old_value: {
          ciudad_id: proveedorFind.ciudad_id,
          observaciones: proveedorFind.observaciones,
          activo: proveedorFind.activo,
        },
      });

      return { message: 'Proveedor eliminado exitosamente', data: proveedor };
    } catch (error) {
      if (error instanceof NotFoundException) throw error;
      if (error instanceof ConflictException) throw error;
      const message = error instanceof Error ? error.message : 'Error desconocido';
      throw new Error(`Error al eliminar proveedor: ${message}`);
    }
  }

  /**
   * Reemplaza el set completo de rubros del proveedor.
   *
   * Lista vacía = proveedor de **todos** los rubros, que es el estado por
   * defecto y el correcto para quien le vende a las dos sucursales.
   * Ver docs/plan-rubros-multi-sucursal.md.
   */
  async setRubros(id: string, rubro_ids: string[], empresa_id: string, user_id?: string) {
    const proveedor = await this.prisma.proveedores.findFirst({ where: { id, empresa_id } });
    if (!proveedor) throw new NotFoundException('Proveedor no encontrado');

    const ids = [...new Set(rubro_ids ?? [])];
    if (ids.length) {
      const propios = await this.prisma.rubros.findMany({
        where: { id: { in: ids }, empresa_id },
        select: { id: true },
      });
      if (propios.length !== ids.length) {
        throw new NotFoundException('Alguno de los rubros no pertenece a la empresa');
      }
    }

    await this.prisma.$transaction(async (tx) => {
      await tx.proveedor_rubros.deleteMany({ where: { proveedor_id: id } });
      if (ids.length) {
        await tx.proveedor_rubros.createMany({
          data: ids.map((rubro_id) => ({ proveedor_id: id, rubro_id })),
        });
      }
    });

    await this.auditService.log({
      empresa_id,
      user_id,
      action: 'UPDATE',
      entity_type: 'proveedor',
      entity_id: id,
      descripcion: ids.length
        ? `Rubros del proveedor actualizados: ${ids.length}`
        : 'Proveedor marcado como de todos los rubros',
      new_value: { rubro_ids: ids },
    });
    return { message: 'Rubros del proveedor actualizados', data: { proveedor_id: id, rubro_ids: ids } };
  }
}
