import { BadRequestException, ConflictException, Injectable, Logger, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';

/**
 * Pool de comisiones del módulo comercial pendientes de revisión/inclusión en la nómina RRHH.
 * La inclusión efectiva en la liquidación la hace el motor (rrhh-engine) leyendo las APROBADAS;
 * al cerrar la liquidación se marcan como INCLUIDA_EN_LIQUIDACION (ver marcarIncluidas).
 */
@Injectable()
export class RrhhComisionesService {
  private readonly logger = new Logger(RrhhComisionesService.name);
  constructor(private readonly prisma: PrismaService) {}

  private async tieneModuloComisiones(empresaId: string): Promise<boolean> {
    const empresa = await this.prisma.empresas.findUnique({ where: { id: empresaId }, select: { type: true } });
    if (empresa?.type === 'holding' || empresa?.type === 'reseller') return true;
    const reg = await this.prisma.suscripcion_modulos.findFirst({
      where: { activo: true, modulos: { codigo: 'COMISIONES' }, suscripciones: { empresa_id: empresaId } },
    });
    return !!reg;
  }

  // ── Importación desde el módulo comercial ──────────────────────────────────
  async importarDesdeModuloComercial(empresaId: string, anio: number, mes: number, importadoPor?: string) {
    if (!(await this.tieneModuloComisiones(empresaId))) {
      throw new BadRequestException('El módulo de Comisiones no está activo para esta empresa.');
    }
    const inicio = new Date(anio, mes - 1, 1);
    const fin = new Date(anio, mes, 0);
    let importadas = 0;
    let yaExistentes = 0;
    let sinVinculo = 0;

    const registrar = async (row: {
      empleadoId: string;
      origen_tipo: 'SUPERVISOR' | 'VENDEDOR' | 'COBRADOR';
      origen_id: string;
      origen_tabla: string;
      comision_origen_id: string;
      monto: number;
      descripcion: string;
    }) => {
      if (row.monto <= 0) return;
      const existe = await this.prisma.rrhh_comisiones_pendientes.findFirst({
        where: {
          comision_origen_id: row.comision_origen_id,
          origen_tabla: row.origen_tabla,
          periodo_anio: anio,
          periodo_mes: mes,
        },
        select: { id: true },
      });
      if (existe) { yaExistentes++; return; }
      await this.prisma.rrhh_comisiones_pendientes.create({
        data: {
          empresa_id: empresaId,
          empleado_id: row.empleadoId,
          periodo_anio: anio,
          periodo_mes: mes,
          origen_tipo: row.origen_tipo,
          origen_id: row.origen_id,
          origen_tabla: row.origen_tabla,
          comision_origen_id: row.comision_origen_id,
          monto_bruto: row.monto,
          descripcion: row.descripcion,
          creado_por: importadoPor ?? null,
        },
      });
      importadas++;
    };

    // 1) Supervisores → comisiones_supervisores (total_comision por período)
    const supComs = await this.prisma.comisiones_supervisores.findMany({
      where: { empresa_id: empresaId, periodo_desde: { lte: fin }, periodo_hasta: { gte: inicio } },
      include: { supervisor: { select: { id: true, empleado_id: true, nombre: true, apellido: true } } },
    });
    for (const c of supComs) {
      if (!c.supervisor?.empleado_id) { sinVinculo++; continue; }
      await registrar({
        empleadoId: c.supervisor.empleado_id,
        origen_tipo: 'SUPERVISOR',
        origen_id: c.supervisor.id,
        origen_tabla: 'comisiones_supervisores',
        comision_origen_id: c.id,
        monto: Number(c.total_comision),
        descripcion: `Comisión supervisor ${[c.supervisor.nombre, c.supervisor.apellido].filter(Boolean).join(' ')}`.trim(),
      });
    }

    // 2) Vendedores/cobradores → liquidaciones_comisiones (tipo=vendedor, total_comisiones)
    const liqComs = await this.prisma.liquidaciones_comisiones.findMany({
      where: {
        empresa_id: empresaId,
        tipo: 'vendedor',
        vendedor_cobrador_id: { not: null },
        periodo_desde: { lte: fin },
        periodo_hasta: { gte: inicio },
      },
    });
    const vendedorIds = [...new Set(liqComs.map((l) => l.vendedor_cobrador_id).filter((v): v is string => !!v))];
    const vendedores = vendedorIds.length
      ? await this.prisma.vendedores_cobradores.findMany({
          where: { id: { in: vendedorIds } },
          select: { id: true, empleado_id: true, tipo: true, nombre: true, apellido: true },
        })
      : [];
    const vendedorMap = new Map(vendedores.map((v) => [v.id, v]));
    for (const l of liqComs) {
      const v = l.vendedor_cobrador_id ? vendedorMap.get(l.vendedor_cobrador_id) : null;
      if (!v?.empleado_id) { sinVinculo++; continue; }
      const origenTipo = (v.tipo || '').toLowerCase() === 'cobrador' ? 'COBRADOR' : 'VENDEDOR';
      await registrar({
        empleadoId: v.empleado_id,
        origen_tipo: origenTipo,
        origen_id: v.id,
        origen_tabla: 'liquidaciones_comisiones',
        comision_origen_id: l.id,
        monto: Number(l.total_comisiones),
        descripcion: `Comisión ${origenTipo.toLowerCase()} ${[v.nombre, v.apellido].filter(Boolean).join(' ')}`.trim(),
      });
    }

    this.logger.log(`Comisiones importadas (${anio}-${mes}, empresa ${empresaId}): ${importadas} nuevas, ${yaExistentes} ya existentes, ${sinVinculo} sin vínculo`);
    return { importadas, yaExistentes, sinVinculo };
  }

  // ── Revisión ───────────────────────────────────────────────────────────────
  async listarPendientesPorPeriodo(
    empresaId: string,
    anio: number,
    mes: number,
    filtros?: { estado?: string; empleadoId?: string },
  ) {
    return this.prisma.rrhh_comisiones_pendientes.findMany({
      where: {
        empresa_id: empresaId,
        periodo_anio: anio,
        periodo_mes: mes,
        ...(filtros?.estado ? { estado: filtros.estado as any } : {}),
        ...(filtros?.empleadoId ? { empleado_id: filtros.empleadoId } : {}),
      },
      include: { empleado: { select: { id: true, numero_empleado: true, nombres: true, apellidos: true } } },
      orderBy: [{ estado: 'asc' }, { created_at: 'desc' }],
    });
  }

  private async getRevisable(id: string, empresaId: string) {
    const c = await this.prisma.rrhh_comisiones_pendientes.findFirst({ where: { id, empresa_id: empresaId } });
    if (!c) throw new NotFoundException('Comisión no encontrada');
    if (c.estado === 'INCLUIDA_EN_LIQUIDACION') {
      throw new ConflictException('La comisión ya fue incluida en una liquidación; no puede modificarse.');
    }
    return c;
  }

  async aprobar(id: string, empresaId: string, userId?: string) {
    await this.getRevisable(id, empresaId);
    return this.prisma.rrhh_comisiones_pendientes.update({
      where: { id },
      data: { estado: 'APROBADA', revisado_por: userId ?? null, fecha_revision: new Date(), motivo_rechazo: null, updated_at: new Date() },
    });
  }

  async rechazar(id: string, empresaId: string, motivo: string, userId?: string) {
    if (!motivo?.trim()) throw new BadRequestException('El motivo de rechazo es obligatorio.');
    await this.getRevisable(id, empresaId);
    return this.prisma.rrhh_comisiones_pendientes.update({
      where: { id },
      data: { estado: 'RECHAZADA', revisado_por: userId ?? null, fecha_revision: new Date(), motivo_rechazo: motivo.trim(), updated_at: new Date() },
    });
  }

  async aprobarMasivo(ids: string[], empresaId: string, userId?: string) {
    const res = await this.prisma.rrhh_comisiones_pendientes.updateMany({
      where: { id: { in: ids }, empresa_id: empresaId, estado: { in: ['PENDIENTE', 'RECHAZADA'] } },
      data: { estado: 'APROBADA', revisado_por: userId ?? null, fecha_revision: new Date(), motivo_rechazo: null, updated_at: new Date() },
    });
    return { aprobadas: res.count };
  }

  // ── Vinculación supervisor/vendedor → empleado RRHH ────────────────────────
  async listarVinculaciones(empresaId: string) {
    const empSelect = { select: { id: true, numero_empleado: true, nombres: true, apellidos: true } };
    const [supervisores, vendedores] = await Promise.all([
      this.prisma.supervisores.findMany({
        where: { empresa_id: empresaId, active: true },
        select: { id: true, nombre: true, apellido: true, empleado_id: true, empleado: empSelect },
        orderBy: [{ nombre: 'asc' }],
      }),
      this.prisma.vendedores_cobradores.findMany({
        where: { empresa_id: empresaId, active: true },
        select: { id: true, nombre: true, apellido: true, tipo: true, empleado_id: true, empleado: empSelect },
        orderBy: [{ nombre: 'asc' }],
      }),
    ]);
    return { supervisores, vendedores };
  }

  async vincularEmpleado(empresaId: string, tipo: string, id: string, empleadoId: string | null) {
    if (empleadoId) {
      const emp = await this.prisma.rrhh_empleados.findFirst({ where: { id: empleadoId, empresa_id: empresaId }, select: { id: true } });
      if (!emp) throw new BadRequestException('Empleado inválido para esta empresa.');
    }
    const t = (tipo || '').toUpperCase();
    if (t === 'SUPERVISOR') {
      const s = await this.prisma.supervisores.findFirst({ where: { id, empresa_id: empresaId }, select: { id: true } });
      if (!s) throw new NotFoundException('Supervisor no encontrado.');
      return this.prisma.supervisores.update({ where: { id }, data: { empleado_id: empleadoId, updated_at: new Date() } });
    }
    if (t === 'VENDEDOR') {
      const v = await this.prisma.vendedores_cobradores.findFirst({ where: { id, empresa_id: empresaId }, select: { id: true } });
      if (!v) throw new NotFoundException('Vendedor/cobrador no encontrado.');
      return this.prisma.vendedores_cobradores.update({ where: { id }, data: { empleado_id: empleadoId, updated_at: new Date() } });
    }
    throw new BadRequestException('Tipo inválido (use supervisor o vendedor).');
  }

  // ── Inclusión en liquidación (motor lee APROBADAS; acá se marcan al cerrar) ──
  /**
   * Devuelve las comisiones APROBADAS del período para los empleados dados, agrupadas por empleado.
   * Usado por el motor para agregar las líneas COMISION_VENTAS antes del cálculo de IPS.
   */
  async aprobadasPorEmpleado(empresaId: string, anio: number, mes: number) {
    const rows = await this.prisma.rrhh_comisiones_pendientes.findMany({
      where: { empresa_id: empresaId, periodo_anio: anio, periodo_mes: mes, estado: 'APROBADA' },
      select: { id: true, empleado_id: true, monto_bruto: true, descripcion: true },
    });
    const map = new Map<string, { id: string; monto: number; descripcion: string | null }[]>();
    for (const r of rows) {
      const arr = map.get(r.empleado_id) ?? [];
      arr.push({ id: r.id, monto: Number(r.monto_bruto), descripcion: r.descripcion });
      map.set(r.empleado_id, arr);
    }
    return map;
  }

  /**
   * Marca como INCLUIDA_EN_LIQUIDACION las comisiones cuyo id aparece como referencia_origen_id
   * en las líneas COMISION de la liquidación dada. Llamado al cerrar.
   */
  async marcarIncluidas(liquidacionId: string, empresaId: string) {
    const lineas = await this.prisma.rrhh_liquidaciones_detalle.findMany({
      where: { liquidacion_id: liquidacionId, origen: 'COMISION', referencia_origen_id: { not: null } },
      select: { id: true, referencia_origen_id: true },
    });
    let marcadas = 0;
    for (const l of lineas) {
      if (!l.referencia_origen_id) continue;
      const res = await this.prisma.rrhh_comisiones_pendientes.updateMany({
        where: { id: l.referencia_origen_id, empresa_id: empresaId, estado: 'APROBADA' },
        data: { estado: 'INCLUIDA_EN_LIQUIDACION', liquidacion_id: liquidacionId, detalle_id: l.id, updated_at: new Date() },
      });
      marcadas += res.count;
    }
    return { marcadas };
  }
}
