import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
import { EstadoSuscripcion } from 'src/suscripciones/dto/enum-sucripcion';
import { PrismaService } from '../prisma/prisma.service';

export type PlanLimitType =
  | 'usuarios'
  | 'productos'
  | 'clientes'
  | 'documentos'
  | 'monto_facturacion'
  | 'documentos_prueba'
  | 'empresas'
  | 'sucursales';

interface PlanSnapshot {
  max_user: number | null;
  max_producto: number | null;
  max_documentos: number | null;
  max_clientes: number | null;
  monto_facturacion: number | null;
  max_ducumento_prueba: number | null;
  max_empresas: number | null;
  max_sucursales: number | null;
  periodo_prueba: number | null;
  descripcion: string | null;
}

export interface LimiteOverride {
  max_user?: number;
  max_producto?: number;
  max_documentos?: number;
  max_clientes?: number;
  monto_facturacion?: number;
  max_empresas?: number;
  max_sucursales?: number;
}

@Injectable()
export class PlanLimitsService {
  constructor(private readonly prisma: PrismaService) {}

  // ─── Obtiene suscripción activa con override ──────────────────────────────────
  private async getActiveSuscripcion(
    empresa_id: string,
  ): Promise<{ plan: PlanSnapshot | null; override: LimiteOverride | null }> {
    const suscripcion = await (this.prisma.suscripciones as any).findFirst({
      where: {
        empresa_id,
        estado: { in: [EstadoSuscripcion.Activo, EstadoSuscripcion.EnGracia] },
      },
      select: {
        id: true,
        limite_override: true,
        override_expira_at: true,
        override_nota: true,
        planes: {
          select: {
            descripcion: true,
            max_user: true,
            max_producto: true,
            max_documentos: true,
            max_clientes: true,
            monto_facturacion: true,
            max_ducumento_prueba: true,
            max_empresas: true,
            max_sucursales: true,
            periodo_prueba: true,
          },
        },
      },
      orderBy: { created_at: 'desc' },
    });

    if (!suscripcion) return { plan: null, override: null };

    let override: LimiteOverride | null = null;
    if (suscripcion.limite_override && suscripcion.override_expira_at) {
      const now = new Date();
      if (new Date(suscripcion.override_expira_at) > now) {
        override = suscripcion.limite_override as LimiteOverride;
      }
    } else if (suscripcion.limite_override && !suscripcion.override_expira_at) {
      // Override permanente (sin fecha de expiración)
      override = suscripcion.limite_override as LimiteOverride;
    }

    if (!suscripcion.planes) return { plan: null, override };

    const p = suscripcion.planes;
    // Helper: convierte Decimal/number a number, y trata 0 como null (sin límite)
    const toLimit = (v: unknown): number | null => {
      if (v == null) return null;
      const n = Number(v);
      return n > 0 ? n : null; // 0 o negativo = sin límite
    };
    const plan: PlanSnapshot = {
      max_user: toLimit(p.max_user),
      max_producto: toLimit(p.max_producto),
      max_documentos: toLimit(p.max_documentos),
      max_clientes: toLimit(p.max_clientes),
      monto_facturacion: toLimit(p.monto_facturacion),
      max_ducumento_prueba: toLimit(p.max_ducumento_prueba),
      max_empresas: toLimit(p.max_empresas),
      max_sucursales: toLimit(p.max_sucursales),
      periodo_prueba: p.periodo_prueba ? Number(p.periodo_prueba) : null,
      descripcion: p.descripcion ?? null,
    };

    return { plan, override };
  }

  // Resuelve el límite efectivo: override tiene prioridad si es > 0, 0 o null = sin límite
  private resolveLimit(planValue: number | null, overrideValue?: number): number | null {
    if (overrideValue != null && overrideValue > 0) return overrideValue;
    if (planValue != null && planValue <= 0) return null; // 0 = sin límite
    return planValue;
  }

  // ─── Contadores actuales por empresa ─────────────────────────────────────────
  private async countUsuarios(empresa_id: string): Promise<number> {
    return this.prisma.usuario.count({ where: { empresa_id, active: true } });
  }

  private async countProductos(empresa_id: string): Promise<number> {
    return this.prisma.productos.count({ where: { empresa_id, deleted: false } });
  }

  private async countClientes(empresa_id: string): Promise<number> {
    return this.prisma.clientes.count({
      where: { deleted: false, personas: { empresa_id } },
    });
  }

  private async countDocumentos(empresa_id: string): Promise<number> {
    const now = new Date();
    const desde = new Date(now.getFullYear(), now.getMonth(), 1);
    return this.prisma.factura_cab.count({
      where: { empresa_id, created_at: { gte: desde } },
    });
  }

  private async countDocumentosPrueba(empresa_id: string): Promise<number> {
    return this.prisma.factura_cab.count({
      where: { empresa_id, cdc: { startsWith: '2' } },
    });
  }

  private async countEmpresasHijas(empresa_id: string): Promise<number> {
    return this.prisma.empresas.count({ where: { parent_id: empresa_id, active: true } });
  }

  private async countSucursales(empresa_id: string): Promise<number> {
    return this.prisma.empresas_sucursales.count({ where: { empresa_id, active: true } });
  }

  private async sumMontoFacturacion(empresa_id: string): Promise<number> {
    const now = new Date();
    const desde = new Date(now.getFullYear(), now.getMonth(), 1);

    const [pyg, otras] = await Promise.all([
      this.prisma.factura_subtotales.aggregate({
        where: { factura_cab: { empresa_id, created_at: { gte: desde }, moneda: { codigo: 'PYG' } } },
        _sum: { dtotope: true },
      }),
      this.prisma.factura_subtotales.aggregate({
        where: { factura_cab: { empresa_id, created_at: { gte: desde }, moneda: { codigo: { not: 'PYG' } } } },
        _sum: { dtotalgs: true },
      }),
    ]);

    return Number(pyg._sum.dtotope ?? 0) + Number(otras._sum.dtotalgs ?? 0);
  }

  // ─── Validación principal ─────────────────────────────────────────────────────
  async checkLimit(empresa_id: string, tipo: PlanLimitType): Promise<void> {
    const { plan, override } = await this.getActiveSuscripcion(empresa_id);
    if (!plan) return;

    switch (tipo) {
      case 'usuarios': {
        const maximo = this.resolveLimit(plan.max_user, override?.max_user);
        if (maximo === null) return;
        const actual = await this.countUsuarios(empresa_id);
        if (actual >= maximo) {
          throw new ForbiddenException(
            `Límite de usuarios alcanzado (${actual}/${maximo}) según el plan "${plan.descripcion}". Actualizá tu plan para agregar más usuarios.`,
          );
        }
        break;
      }

      case 'productos': {
        const maximo = this.resolveLimit(plan.max_producto, override?.max_producto);
        if (maximo === null) return;
        const actual = await this.countProductos(empresa_id);
        if (actual >= maximo) {
          throw new ForbiddenException(
            `Límite de productos alcanzado (${actual}/${maximo}) según el plan "${plan.descripcion}". Actualizá tu plan para agregar más productos.`,
          );
        }
        break;
      }

      case 'clientes': {
        const maximo = this.resolveLimit(plan.max_clientes, override?.max_clientes);
        if (maximo === null) return;
        const actual = await this.countClientes(empresa_id);
        if (actual >= maximo) {
          throw new ForbiddenException(
            `Límite de clientes alcanzado (${actual}/${maximo}) según el plan "${plan.descripcion}". Actualizá tu plan para agregar más clientes.`,
          );
        }
        break;
      }

      case 'documentos': {
        const maximo = this.resolveLimit(plan.max_documentos, override?.max_documentos);
        if (maximo === null) return;
        const actual = await this.countDocumentos(empresa_id);
        if (actual >= maximo) {
          throw new ForbiddenException(
            `Límite de documentos del mes alcanzado (${actual}/${maximo}) según el plan "${plan.descripcion}". Actualizá tu plan para emitir más documentos.`,
          );
        }
        break;
      }

      case 'monto_facturacion': {
        const maximo = this.resolveLimit(plan.monto_facturacion, override?.monto_facturacion);
        if (maximo === null) return;
        const actual = await this.sumMontoFacturacion(empresa_id);
        if (actual >= maximo) {
          throw new ForbiddenException(
            `Límite de monto de facturación mensual alcanzado (${actual.toLocaleString('es-PY')} / ${maximo.toLocaleString('es-PY')} Gs.) según el plan "${plan.descripcion}". Actualizá tu plan para continuar facturando.`,
          );
        }
        break;
      }

      case 'documentos_prueba': {
        if (plan.max_ducumento_prueba === null) return;
        const actual = await this.countDocumentosPrueba(empresa_id);
        if (actual >= plan.max_ducumento_prueba) {
          throw new ForbiddenException(
            `Límite de documentos de prueba alcanzado (${actual}/${plan.max_ducumento_prueba}) según el plan "${plan.descripcion}". Contactá a soporte para ampliar el límite de prueba.`,
          );
        }
        break;
      }

      case 'empresas': {
        const maximo = this.resolveLimit(plan.max_empresas, override?.max_empresas);
        if (maximo === null) return;
        const actual = await this.countEmpresasHijas(empresa_id);
        if (actual >= maximo) {
          throw new ForbiddenException(
            `Límite de empresas alcanzado (${actual}/${maximo}) según el plan "${plan.descripcion}". Actualizá tu plan para agregar más empresas.`,
          );
        }
        break;
      }

      case 'sucursales': {
        const maximo = this.resolveLimit(plan.max_sucursales, override?.max_sucursales);
        if (maximo === null) return;
        const actual = await this.countSucursales(empresa_id);
        if (actual >= maximo) {
          throw new ForbiddenException(
            `Límite de sucursales alcanzado (${actual}/${maximo}) según el plan "${plan.descripcion}". Actualizá tu plan para agregar más sucursales.`,
          );
        }
        break;
      }
    }
  }

  // ─── Aplicar override temporal de límites ─────────────────────────────────────
  async applyOverride(
    empresa_id: string,
    overrideLimites: LimiteOverride,
    expira_at: Date | null,
    nota: string | null,
  ) {
    const suscripcion = await this.prisma.suscripciones.findFirst({
      where: {
        empresa_id,
        estado: { in: [EstadoSuscripcion.Activo, EstadoSuscripcion.EnGracia] },
      },
      orderBy: { created_at: 'desc' },
    });

    if (!suscripcion) {
      throw new NotFoundException('No se encontró suscripción activa para esta empresa');
    }

    const updated = await (this.prisma.suscripciones as any).update({
      where: { id: suscripcion.id },
      data: {
        limite_override: overrideLimites,
        override_expira_at: expira_at,
        override_nota: nota,
        updated_at: new Date(),
      },
    });

    return {
      suscripcion_id: updated.id,
      limite_override: overrideLimites,
      override_expira_at: expira_at,
      override_nota: nota,
    };
  }

  // ─── Limpiar override ──────────────────────────────────────────────────────────
  async clearOverride(empresa_id: string) {
    const suscripcion = await this.prisma.suscripciones.findFirst({
      where: {
        empresa_id,
        estado: { in: [EstadoSuscripcion.Activo, EstadoSuscripcion.EnGracia] },
      },
      orderBy: { created_at: 'desc' },
    });

    if (!suscripcion) {
      throw new NotFoundException('No se encontró suscripción activa para esta empresa');
    }

    await (this.prisma.suscripciones as any).update({
      where: { id: suscripcion.id },
      data: { limite_override: null, override_expira_at: null, override_nota: null, updated_at: new Date() },
    });

    return { message: 'Override eliminado correctamente' };
  }

  // ─── Información de uso (para mostrar en UI) ──────────────────────────────────
  async getUsageInfo(empresa_id: string) {
    const { plan, override } = await this.getActiveSuscripcion(empresa_id);

    const [usuarios, productos, clientes, documentos, monto, docsPrueba, empresasHijas, sucursales] = await Promise.all(
      [
        this.countUsuarios(empresa_id),
        this.countProductos(empresa_id),
        this.countClientes(empresa_id),
        this.countDocumentos(empresa_id),
        this.sumMontoFacturacion(empresa_id),
        this.countDocumentosPrueba(empresa_id),
        this.countEmpresasHijas(empresa_id),
        this.countSucursales(empresa_id),
      ],
    );

    return {
      plan: plan?.descripcion ?? null,
      override_activo: override !== null,
      override_nota: null as string | null,
      limites: {
        usuarios: {
          actual: usuarios,
          maximo: this.resolveLimit(plan?.max_user ?? null, override?.max_user),
          override: override?.max_user ?? null,
        },
        productos: {
          actual: productos,
          maximo: this.resolveLimit(plan?.max_producto ?? null, override?.max_producto),
          override: override?.max_producto ?? null,
        },
        clientes: {
          actual: clientes,
          maximo: this.resolveLimit(plan?.max_clientes ?? null, override?.max_clientes),
          override: override?.max_clientes ?? null,
        },
        documentos_mes: {
          actual: documentos,
          maximo: this.resolveLimit(plan?.max_documentos ?? null, override?.max_documentos),
          override: override?.max_documentos ?? null,
        },
        monto_facturacion: {
          actual: monto,
          maximo: this.resolveLimit(plan?.monto_facturacion ?? null, override?.monto_facturacion),
          override: override?.monto_facturacion ?? null,
        },
        documentos_prueba: { actual: docsPrueba, maximo: plan?.max_ducumento_prueba ?? null, override: null },
        empresas: {
          actual: empresasHijas,
          maximo: this.resolveLimit(plan?.max_empresas ?? null, override?.max_empresas),
          override: override?.max_empresas ?? null,
        },
        sucursales: {
          actual: sucursales,
          maximo: this.resolveLimit(plan?.max_sucursales ?? null, override?.max_sucursales),
          override: override?.max_sucursales ?? null,
        },
      },
    };
  }
}
