import {
    BadRequestException,
    Injectable,
    NotFoundException,
} from '@nestjs/common';
import { Decimal } from '@prisma/client/runtime/library';
import { PrismaService } from '../prisma/prisma.service';
import { JerarquiaEmpresasService } from './jerarquia-empresas.service';

export interface ComisionCalculada {
  suscripcion_id: string;
  empresa_beneficiaria_id: string;
  empresa_pagadora_id: string;
  tipo_comision: string;
  porcentaje: number;
  monto_base: number;
  monto_comision: number;
  periodo_inicio: Date;
  periodo_fin: Date;
}

export interface ResumenComisiones {
  total_comisiones: number;
  comisiones_pendientes: number;
  comisiones_pagadas: number;
  comisiones_por_tipo: {
    mensual: number;
    venta_inicial: number;
    renovacion: number;
    upgrade: number;
  };
}

@Injectable()
export class ComisionesService {
  constructor(
    private prisma: PrismaService,
    private jerarquiaService: JerarquiaEmpresasService,
  ) {}

  /**
   * Calcula y genera comisiones automáticamente para una suscripción
   */
  async calcularComisionesSuscripcion(
    suscripcionId: string,
    tipoComision = 'mensual',
  ): Promise<ComisionCalculada[]> {
    const suscripcion = await this.prisma.suscripciones.findUnique({
      where: { id: suscripcionId },
      include: {
        empresas: {
          select: {
            id: true,
            parent_id: true,
            type: true,
            ruc: true,
            razon_social: true,
          },
        },
        planes: {
          select: {
            id: true,
            precio: true,
            descripcion: true,
          },
        },
      },
    });

    if (!suscripcion) {
      throw new NotFoundException('Suscripción no encontrada');
    }

    if (!suscripcion.empresas) {
      throw new BadRequestException('La suscripción no tiene empresa asociada');
    }

    const comisionesCalculadas: ComisionCalculada[] = [];
    const empresa = suscripcion.empresas;

    // Si la empresa tiene parent (es subsidiario de un reseller o holding)
    if (empresa.parent_id) {
      const parent = await this.prisma.empresas.findUnique({
        where: { id: empresa.parent_id },
        select: {
          id: true,
          type: true,
          ruc: true,
          razon_social: true,
        },
      });

      if (parent && (parent.type === 'reseller' || parent.type === 'holding')) {
        // Obtener porcentaje de comisión del parent
        const comisionPorcentaje = await this.obtenerPorcentajeComision(
          parent.id,
          suscripcion?.plan_id || undefined,
        );

        if (comisionPorcentaje > 0) {
          const montoBase =
            Number(suscripcion.costo_mensual) ||
            Number(suscripcion.planes?.precio) ||
            0;
          const montoComision = montoBase * (comisionPorcentaje / 100);

          // Calcular período (mes actual)
          const ahora = new Date();
          const periodoInicio = new Date(
            ahora.getFullYear(),
            ahora.getMonth(),
            1,
          );
          const periodoFin = new Date(
            ahora.getFullYear(),
            ahora.getMonth() + 1,
            0,
          );

          const comision: ComisionCalculada = {
            suscripcion_id: suscripcionId,
            empresa_beneficiaria_id: parent.id,
            empresa_pagadora_id: empresa.id,
            tipo_comision: tipoComision,
            porcentaje: comisionPorcentaje,
            monto_base: montoBase,
            monto_comision: montoComision,
            periodo_inicio: periodoInicio,
            periodo_fin: periodoFin,
          };

          comisionesCalculadas.push(comision);
        }
      }
    }

    return comisionesCalculadas;
  }

  /**
   * Genera comisiones automáticamente y las guarda en la base de datos
   */
  async generarComisiones(
    suscripcionId: string,
    tipoComision = 'mensual',
  ): Promise<void> {
    const comisionesCalculadas = await this.calcularComisionesSuscripcion(
      suscripcionId,
      tipoComision,
    );

    for (const comision of comisionesCalculadas) {
      // Verificar si ya existe una comisión para este período
      const comisionExistente = await this.prisma.comisiones_nuevas.findFirst({
        where: {
          suscripcion_id: comision.suscripcion_id,
          empresa_beneficiaria_id: comision.empresa_beneficiaria_id,
          tipo_comision: comision.tipo_comision,
          periodo_inicio: comision.periodo_inicio,
          periodo_fin: comision.periodo_fin,
        },
      });

      if (!comisionExistente) {
        await this.prisma.comisiones_nuevas.create({
          data: {
            suscripcion_id: comision.suscripcion_id,
            empresa_beneficiaria_id: comision.empresa_beneficiaria_id,
            empresa_pagadora_id: comision.empresa_pagadora_id,
            tipo_comision: comision.tipo_comision,
            porcentaje: new Decimal(comision.porcentaje),
            monto_base: new Decimal(comision.monto_base),
            monto_comision: new Decimal(comision.monto_comision),
            periodo_inicio: comision.periodo_inicio,
            periodo_fin: comision.periodo_fin,
            estado: 'pendiente',
          },
        });
      }
    }
  }

  /**
   * Genera comisiones mensuales para todas las suscripciones activas
   */
  async generarComisionesMensuales(): Promise<{
    procesadas: number;
    generadas: number;
  }> {
    const suscripcionesActivas = await this.prisma.suscripciones.findMany({
      where: {
        estado: 'Activo',
        empresas: {
          active: true,
          parent_id: { not: null }, // Solo subsidiarios que tienen parent
        },
      },
      select: { id: true },
    });

    let procesadas = 0;
    let generadas = 0;

    for (const suscripcion of suscripcionesActivas) {
      try {
        const comisionesAnteriores = await this.prisma.comisiones_nuevas.count({
          where: { suscripcion_id: suscripcion.id },
        });

        await this.generarComisiones(suscripcion.id, 'mensual');
        procesadas++;

        const comisionesNuevas = await this.prisma.comisiones_nuevas.count({
          where: { suscripcion_id: suscripcion.id },
        });

        generadas += comisionesNuevas - comisionesAnteriores;
      } catch (error) {
        console.error(`Error procesando suscripción ${suscripcion.id}:`, error);
      }
    }

    return { procesadas, generadas };
  }

  /**
   * Obtiene el porcentaje de comisión para un reseller y plan específico
   */
  private async obtenerPorcentajeComision(
    resellerEmpresaId: string,
    planId?: string,
  ): Promise<number> {
    // Primero buscar en precios específicos del reseller
    if (planId) {
      const precioReseller = await this.prisma.precios_reseller.findFirst({
        where: {
          reseller_empresa_id: resellerEmpresaId,
          plan_id: planId,
          activo: true,
          fecha_inicio: { lte: new Date() },
          OR: [{ fecha_fin: null }, { fecha_fin: { gte: new Date() } }],
        },
        select: { descuento_porcentaje: true },
      });

      if (precioReseller && precioReseller.descuento_porcentaje) {
        return Number(precioReseller.descuento_porcentaje);
      }
    }

    // Si no hay precio específico, usar el porcentaje general del reseller
    const reseller = await this.prisma.empresas.findUnique({
      where: { id: resellerEmpresaId },
      select: { comision_porcentaje: true },
    });

    return Number(reseller?.comision_porcentaje) || 0;
  }

  /**
   * Obtiene resumen de comisiones para un reseller
   */
  async getResumenComisionesReseller(
    resellerEmpresaId: string,
  ): Promise<ResumenComisiones> {
    const comisiones = await this.prisma.comisiones_nuevas.findMany({
      where: { empresa_beneficiaria_id: resellerEmpresaId },
      select: {
        monto_comision: true,
        estado: true,
        tipo_comision: true,
      },
    });

    const totalComisiones = comisiones.reduce(
      (total, c) => total + Number(c.monto_comision),
      0,
    );

    const comisionesPendientes = comisiones
      .filter((c) => c.estado === 'pendiente')
      .reduce((total, c) => total + Number(c.monto_comision), 0);

    const comisionesPagadas = comisiones
      .filter((c) => c.estado === 'pagada')
      .reduce((total, c) => total + Number(c.monto_comision), 0);

    const comisionesPorTipo = {
      mensual: comisiones
        .filter((c) => c.tipo_comision === 'mensual')
        .reduce((total, c) => total + Number(c.monto_comision), 0),
      venta_inicial: comisiones
        .filter((c) => c.tipo_comision === 'venta_inicial')
        .reduce((total, c) => total + Number(c.monto_comision), 0),
      renovacion: comisiones
        .filter((c) => c.tipo_comision === 'renovacion')
        .reduce((total, c) => total + Number(c.monto_comision), 0),
      upgrade: comisiones
        .filter((c) => c.tipo_comision === 'upgrade')
        .reduce((total, c) => total + Number(c.monto_comision), 0),
    };

    return {
      total_comisiones: totalComisiones,
      comisiones_pendientes: comisionesPendientes,
      comisiones_pagadas: comisionesPagadas,
      comisiones_por_tipo: comisionesPorTipo,
    };
  }

  /**
   * Marca comisiones como pagadas
   */
  async marcarComisionesPagadas(
    comisionIds: string[],
    metodoPago?: string,
    referenciaPago?: string,
  ): Promise<void> {
    await this.prisma.comisiones_nuevas.updateMany({
      where: { id: { in: comisionIds } },
      data: {
        estado: 'pagada',
        fecha_pago: new Date(),
        metodo_pago: metodoPago,
        referencia_pago: referenciaPago,
        updated_at: new Date(),
      },
    });
  }

  /**
   * Obtiene comisiones pendientes de un reseller
   */
  async getComisionesPendientes(resellerEmpresaId: string) {
    return this.prisma.comisiones_nuevas.findMany({
      where: {
        empresa_beneficiaria_id: resellerEmpresaId,
        estado: 'pendiente',
      },
      include: {
        suscripciones: {
          include: {
            empresas: {
              select: {
                ruc: true,
                razon_social: true,
              },
            },
            planes: {
              select: {
                descripcion: true,
              },
            },
          },
        },
      },
      orderBy: { periodo_inicio: 'desc' },
    });
  }

  /**
   * Obtiene historial de comisiones pagadas
   */
  async getHistorialComisiones(resellerEmpresaId: string, limite = 50) {
    return this.prisma.comisiones_nuevas.findMany({
      where: {
        empresa_beneficiaria_id: resellerEmpresaId,
        estado: 'pagada',
      },
      include: {
        suscripciones: {
          include: {
            empresas: {
              select: {
                ruc: true,
                razon_social: true,
              },
            },
            planes: {
              select: {
                descripcion: true,
              },
            },
          },
        },
      },
      orderBy: { fecha_pago: 'desc' },
      take: limite,
    });
  }

  // ============================================================
  // Endpoints para HOLDING — Vista global de comisiones
  // ============================================================

  /**
   * Resumen global de comisiones para el holding
   * Muestra totales pendientes, pagados este mes, cantidad de resellers, etc.
   */
  async getResumenComisionesHolding(holdingEmpresaId: string) {
    // Obtener todos los subsidiarios directos e indirectos del holding
    const subsidiarios = await this.prisma.empresas.findMany({
      where: { parent_id: holdingEmpresaId },
      select: { id: true, type: true },
    });

    const resellerIds = subsidiarios
      .filter((s) => s.type === 'reseller')
      .map((s) => s.id);

    // También incluir al holding como beneficiario directo
    const beneficiarioIds = [holdingEmpresaId, ...resellerIds];

    // Todas las comisiones donde el beneficiario es un reseller hijo del holding
    const comisiones = await this.prisma.comisiones_nuevas.findMany({
      where: { empresa_beneficiaria_id: { in: beneficiarioIds } },
      select: {
        monto_comision: true,
        estado: true,
        tipo_comision: true,
        fecha_pago: true,
        empresa_beneficiaria_id: true,
      },
    });

    const ahora = new Date();
    const inicioMes = new Date(ahora.getFullYear(), ahora.getMonth(), 1);

    const totalPendiente = comisiones
      .filter((c) => c.estado === 'pendiente')
      .reduce((t, c) => t + Number(c.monto_comision), 0);

    const totalPagadoEsteMes = comisiones
      .filter(
        (c) =>
          c.estado === 'pagada' && c.fecha_pago && c.fecha_pago >= inicioMes,
      )
      .reduce((t, c) => t + Number(c.monto_comision), 0);

    const totalPagadoHistorico = comisiones
      .filter((c) => c.estado === 'pagada')
      .reduce((t, c) => t + Number(c.monto_comision), 0);

    const totalGeneral = comisiones.reduce(
      (t, c) => t + Number(c.monto_comision),
      0,
    );

    // Resellers con comisiones pendientes (únicos)
    const resellersPendientes = new Set(
      comisiones
        .filter((c) => c.estado === 'pendiente')
        .map((c) => c.empresa_beneficiaria_id),
    ).size;

    const cantidadPendientes = comisiones.filter(
      (c) => c.estado === 'pendiente',
    ).length;
    const cantidadPagadas = comisiones.filter(
      (c) => c.estado === 'pagada',
    ).length;

    // Por tipo
    const porTipo = {
      mensual: comisiones
        .filter(
          (c) => c.tipo_comision === 'mensual' && c.estado === 'pendiente',
        )
        .reduce((t, c) => t + Number(c.monto_comision), 0),
      venta_inicial: comisiones
        .filter(
          (c) =>
            c.tipo_comision === 'venta_inicial' && c.estado === 'pendiente',
        )
        .reduce((t, c) => t + Number(c.monto_comision), 0),
      renovacion: comisiones
        .filter(
          (c) => c.tipo_comision === 'renovacion' && c.estado === 'pendiente',
        )
        .reduce((t, c) => t + Number(c.monto_comision), 0),
      upgrade: comisiones
        .filter(
          (c) => c.tipo_comision === 'upgrade' && c.estado === 'pendiente',
        )
        .reduce((t, c) => t + Number(c.monto_comision), 0),
    };

    return {
      total_pendiente: totalPendiente,
      total_pagado_mes: totalPagadoEsteMes,
      total_pagado_historico: totalPagadoHistorico,
      total_general: totalGeneral,
      resellers_con_pendientes: resellersPendientes,
      cantidad_pendientes: cantidadPendientes,
      cantidad_pagadas: cantidadPagadas,
      pendiente_por_tipo: porTipo,
    };
  }

  /**
   * Obtener comisiones pendientes agrupadas por reseller
   * Para la vista principal del holding: lista de resellers con montos
   */
  async getComisionesAgrupadasPorReseller(
    holdingEmpresaId: string,
    estado?: string,
  ) {
    const subsidiarios = await this.prisma.empresas.findMany({
      where: { parent_id: holdingEmpresaId },
      select: { id: true, type: true, razon_social: true, ruc: true },
    });

    const resellerIds = subsidiarios
      .filter((s) => s.type === 'reseller')
      .map((s) => s.id);

    // Incluir holding como beneficiario directo (subsidiarios directos generan comisión al holding)
    const beneficiarioIds = [holdingEmpresaId, ...resellerIds];

    const whereEstado: any = {
      empresa_beneficiaria_id: { in: beneficiarioIds },
    };
    if (estado && estado !== 'todos') {
      whereEstado.estado = estado;
    }

    const comisiones = await this.prisma.comisiones_nuevas.findMany({
      where: whereEstado,
      select: {
        empresa_beneficiaria_id: true,
        monto_comision: true,
        estado: true,
        tipo_comision: true,
      },
    });

    // Mapa de empresa -> info
    const empresaMap = new Map<
      string,
      { razon_social: string; ruc: string; type: string }
    >();
    for (const s of subsidiarios) {
      empresaMap.set(s.id, {
        razon_social: s.razon_social || '',
        ruc: s.ruc || '',
        type: s.type || '',
      });
    }
    // Añadir holding mismo
    const holding = await this.prisma.empresas.findUnique({
      where: { id: holdingEmpresaId },
      select: { razon_social: true, ruc: true, type: true },
    });
    if (holding) {
      empresaMap.set(holdingEmpresaId, {
        razon_social: holding.razon_social || '',
        ruc: holding.ruc || '',
        type: holding.type || 'holding',
      });
    }

    // Agrupar
    const grouped = new Map<
      string,
      { pendiente: number; pagada: number; total: number; cantidad: number }
    >();
    for (const c of comisiones) {
      const key = c.empresa_beneficiaria_id;
      if (!grouped.has(key)) {
        grouped.set(key, { pendiente: 0, pagada: 0, total: 0, cantidad: 0 });
      }
      const g = grouped.get(key)!;
      const monto = Number(c.monto_comision);
      g.total += monto;
      g.cantidad++;
      if (c.estado === 'pendiente') g.pendiente += monto;
      if (c.estado === 'pagada') g.pagada += monto;
    }

    const resultado = Array.from(grouped.entries()).map(
      ([empresaId, montos]) => ({
        empresa_id: empresaId,
        razon_social: empresaMap.get(empresaId)?.razon_social || 'Desconocido',
        ruc: empresaMap.get(empresaId)?.ruc || '',
        type: empresaMap.get(empresaId)?.type || '',
        ...montos,
      }),
    );

    // Ordenar por pendiente descendente
    resultado.sort((a, b) => b.pendiente - a.pendiente);

    return resultado;
  }

  /**
   * Obtener comisiones individuales de un beneficiario específico
   * Para cuando el holding hace clic en un reseller y ve el detalle
   */
  async getComisionesDeReseller(
    resellerEmpresaId: string,
    filtros?: { estado?: string; limite?: number },
  ) {
    const whereClause: any = { empresa_beneficiaria_id: resellerEmpresaId };
    if (filtros?.estado && filtros.estado !== 'todos') {
      whereClause.estado = filtros.estado;
    }

    return this.prisma.comisiones_nuevas.findMany({
      where: whereClause,
      include: {
        suscripciones: {
          include: {
            empresas: {
              select: {
                ruc: true,
                razon_social: true,
              },
            },
            planes: {
              select: {
                descripcion: true,
                codigo: true,
              },
            },
          },
        },
      },
      orderBy: { periodo_inicio: 'desc' },
      take: filtros?.limite || 100,
    });
  }

  /**
   * Historial de pagos realizados por el holding
   * Muestra todos los pagos con datos del beneficiario
   */
  async getHistorialPagosHolding(holdingEmpresaId: string, limite = 50) {
    const subsidiarios = await this.prisma.empresas.findMany({
      where: { parent_id: holdingEmpresaId },
      select: { id: true },
    });

    const beneficiarioIds = [
      holdingEmpresaId,
      ...subsidiarios.map((s) => s.id),
    ];

    return this.prisma.comisiones_nuevas.findMany({
      where: {
        empresa_beneficiaria_id: { in: beneficiarioIds },
        estado: 'pagada',
      },
      include: {
        empresa_beneficiaria: {
          select: {
            ruc: true,
            razon_social: true,
          },
        },
        suscripciones: {
          include: {
            empresas: {
              select: {
                ruc: true,
                razon_social: true,
              },
            },
            planes: {
              select: {
                descripcion: true,
              },
            },
          },
        },
      },
      orderBy: { fecha_pago: 'desc' },
      take: limite,
    });
  }

  /**
   * Procesa comisiones por upgrade de plan
   */
  async procesarComisionUpgrade(
    suscripcionId: string,
    planAnteriorId: string,
    planNuevoId: string,
  ): Promise<void> {
    const suscripcion = await this.prisma.suscripciones.findUnique({
      where: { id: suscripcionId },
      include: {
        empresas: { select: { parent_id: true } },
      },
    });

    if (!suscripcion?.empresas?.parent_id) return;

    const planAnterior = await this.prisma.planes.findUnique({
      where: { id: planAnteriorId },
      select: { precio: true },
    });

    const planNuevo = await this.prisma.planes.findUnique({
      where: { id: planNuevoId },
      select: { precio: true },
    });

    if (!planAnterior || !planNuevo) return;

    const diferenciaPrecio =
      Number(planNuevo.precio) - Number(planAnterior.precio);

    if (diferenciaPrecio > 0) {
      // Generar comisión por upgrade
      await this.generarComisiones(suscripcionId, 'upgrade');
    }
  }
}
