import { BadRequestException, Injectable, NotFoundException, Logger } from '@nestjs/common';
import { AuditService } from 'src/audit/audit.service';
import { PrismaService } from 'src/prisma/prisma.service';
import { CreatePlaneDto, PlanAddonDto } from './dto/create-plane.dto';
import { UpdatePlaneDto } from './dto/update-plane.dto';

type SyncResult = { agregados: number; eliminados: number };

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

  // ── Listar todos los planes con módulos raíz + addons ──
  async findAll(includeInactive = false) {
    const planes = await this.prisma.planes.findMany({
      where: includeInactive ? {} : { active: true },
      include: {
        plan_modulos: { include: { modulos: true } },
        plan_submodulos_addon: { include: { submodulos: { include: { modulos: true } } } },
        plan_submodulos: { include: { submodulos: { include: { modulos: true } } } },
        _count: { select: { suscripciones: true } },
      },
      orderBy: [{ active: 'desc' }, { precio: 'asc' }],
    });

    return {
      message: 'Planes obtenidos correctamente',
      data: planes.map((p) => this.shapePlan(p)),
    };
  }

  // ── Obtener un plan por ID ──
  async findOne(planId: string) {
    const plan = await this.prisma.planes.findUnique({
      where: { id: planId },
      include: {
        plan_modulos: { include: { modulos: true } },
        plan_submodulos_addon: { include: { submodulos: { include: { modulos: true } } } },
        plan_submodulos: { include: { submodulos: { include: { modulos: true } } } },
        _count: { select: { suscripciones: true } },
      },
    });

    if (!plan) throw new NotFoundException('Plan no encontrado');

    return {
      message: 'Plan obtenido correctamente',
      data: this.shapePlan(plan),
    };
  }

  private shapePlan(p: any) {
    return {
      id: p.id,
      codigo: p.codigo,
      descripcion: p.descripcion,
      precio: p.precio,
      tipo_ciclo: p.tipo_ciclo || 'mensual',
      max_user: p.max_user,
      max_producto: p.max_producto,
      max_documentos: p.max_documentos,
      max_clientes: p.max_clientes,
      monto_facturacion: p.monto_facturacion,
      periodo_prueba: p.periodo_prueba,
      max_ducumento_prueba: p.max_ducumento_prueba,
      max_empresas: p.max_empresas,
      max_sucursales: p.max_sucursales,
      dias_gracia: p.dias_gracia,
      tokens_ia_mensual: p.tokens_ia_mensual ?? 0,
      active: p.active,
      created_at: p.created_at,
      updated_at: p.updated_at,
      modulos: (p.plan_modulos || []).map((pm: any) => ({
        id: pm.modulos.id,
        codigo: pm.modulos.codigo,
        descripcion: pm.modulos.descripcion,
        icono: pm.modulos.icono,
        precio: pm.modulos.precio,
        active: pm.active,
      })),
      submodulos_incluidos: (p.plan_submodulos || []).map((ps: any) => ({
        submodulo_id: ps.submodulo_id,
        codigo: ps.submodulos.codigo,
        descripcion: ps.submodulos.descripcion,
        modulo_id: ps.submodulos.modulo_id,
        modulo_codigo: ps.submodulos.modulos?.codigo,
        active: ps.active,
      })),
      addons: (p.plan_submodulos_addon || []).map((pa: any) => ({
        submodulo_id: pa.submodulo_id,
        codigo: pa.submodulos.codigo,
        descripcion: pa.submodulos.descripcion,
        modulo_id: pa.submodulos.modulo_id,
        modulo_codigo: pa.submodulos.modulos?.codigo,
        precio_default: pa.submodulos.precio_addon,
        precio_override: pa.precio,
        active: pa.active,
      })),
      total_suscripciones: p._count?.suscripciones ?? 0,
    };
  }

  // ── Validaciones helper ──
  private async validarModulosRaiz(ids: string[]) {
    if (!ids?.length) return;
    const rows = await this.prisma.modulos.findMany({
      where: { id: { in: ids }, active: true },
      select: { id: true },
    });
    if (rows.length !== ids.length) {
      throw new BadRequestException('Uno o más módulos raíz no existen o están inactivos');
    }
  }

  private async validarSubmodulosIncluidos(ids: string[]) {
    if (!ids?.length) return;
    const rows = await this.prisma.submodulos.findMany({
      where: { id: { in: ids }, active: true, es_addon: false },
      select: { id: true },
    });
    if (rows.length !== ids.length) {
      throw new BadRequestException('Uno o más submódulos no existen, están inactivos o son addons');
    }
  }

  private async validarAddons(addons: PlanAddonDto[]) {
    if (!addons?.length) return;
    const ids = addons.map((a) => a.submodulo_id);
    const rows = await this.prisma.submodulos.findMany({
      where: { id: { in: ids }, active: true, es_addon: true },
      select: { id: true },
    });
    if (rows.length !== ids.length) {
      throw new BadRequestException('Uno o más addons no existen, están inactivos o no son addons');
    }
  }

  // ── Sincronizar plan_modulos ──
  private async syncModulosRaiz(planId: string, ids: string[]): Promise<SyncResult> {
    const actuales = await this.prisma.plan_modulos.findMany({
      where: { plan_id: planId },
      select: { modulo_id: true },
    });
    const actualesSet = new Set(actuales.map((r) => r.modulo_id));
    const nuevosSet = new Set(ids);

    const eliminar = [...actualesSet].filter((id) => !nuevosSet.has(id));
    const agregar = [...nuevosSet].filter((id) => !actualesSet.has(id));

    if (eliminar.length) {
      await this.prisma.plan_modulos.deleteMany({
        where: { plan_id: planId, modulo_id: { in: eliminar } },
      });
    }
    if (agregar.length) {
      await this.prisma.plan_modulos.createMany({
        data: agregar.map((modulo_id) => ({ plan_id: planId, modulo_id, active: true })),
        skipDuplicates: true,
      });
    }
    return { agregados: agregar.length, eliminados: eliminar.length };
  }

  // ── Sincronizar plan_submodulos (no-addon) ──
  private async syncSubmodulosIncluidos(planId: string, ids: string[]): Promise<SyncResult> {
    const actuales = await this.prisma.plan_submodulos.findMany({
      where: { plan_id: planId },
      select: { submodulo_id: true },
    });
    const actualesSet = new Set(actuales.map((r) => r.submodulo_id));
    const nuevosSet = new Set(ids);

    const eliminar = [...actualesSet].filter((id) => !nuevosSet.has(id));
    const agregar = [...nuevosSet].filter((id) => !actualesSet.has(id));

    if (eliminar.length) {
      await this.prisma.plan_submodulos.deleteMany({
        where: { plan_id: planId, submodulo_id: { in: eliminar } },
      });
    }
    if (agregar.length) {
      await this.prisma.plan_submodulos.createMany({
        data: agregar.map((submodulo_id) => ({ plan_id: planId, submodulo_id, active: true })),
        skipDuplicates: true,
      });
    }
    return { agregados: agregar.length, eliminados: eliminar.length };
  }

  // ── Sincronizar plan_submodulos_addon ──
  private async syncAddons(planId: string, addons: PlanAddonDto[]): Promise<SyncResult> {
    const actuales = await this.prisma.plan_submodulos_addon.findMany({
      where: { plan_id: planId },
      select: { submodulo_id: true, precio: true },
    });
    const actualesMap = new Map(actuales.map((r) => [r.submodulo_id, r.precio]));
    const nuevosMap = new Map(addons.map((a) => [a.submodulo_id, a.precio_override ?? null]));

    const eliminar = [...actualesMap.keys()].filter((id) => !nuevosMap.has(id));
    const agregar = [...nuevosMap.entries()].filter(([id]) => !actualesMap.has(id));
    const actualizar = [...nuevosMap.entries()].filter(([id, precio]) => {
      if (!actualesMap.has(id)) return false;
      const prev = actualesMap.get(id);
      const prevNum = prev === null || prev === undefined ? null : Number(prev);
      const newNum = precio === null || precio === undefined ? null : Number(precio);
      return prevNum !== newNum;
    });

    if (eliminar.length) {
      await this.prisma.plan_submodulos_addon.deleteMany({
        where: { plan_id: planId, submodulo_id: { in: eliminar } },
      });
    }
    if (agregar.length) {
      await this.prisma.plan_submodulos_addon.createMany({
        data: agregar.map(([submodulo_id, precio]) => ({
          plan_id: planId,
          submodulo_id,
          precio: precio === null ? null : precio,
          active: true,
        })),
        skipDuplicates: true,
      });
    }
    for (const [submodulo_id, precio] of actualizar) {
      await this.prisma.plan_submodulos_addon.update({
        where: { plan_id_submodulo_id: { plan_id: planId, submodulo_id } },
        data: { precio: precio === null ? null : precio, updated_at: new Date() },
      });
    }
    return { agregados: agregar.length, eliminados: eliminar.length };
  }

  // ── Crear un plan nuevo ──
  async create(dto: CreatePlaneDto, username?: string, user_id?: string, empresa_id?: string) {
    const existente = await this.prisma.planes.findFirst({
      where: { codigo: dto.codigo },
    });
    if (existente) {
      throw new BadRequestException(`Ya existe un plan con el código "${dto.codigo}"`);
    }

    const modulosRaiz = dto.modulos_raiz_ids ?? dto.modulos ?? [];
    const addons = dto.addons ?? [];
    const submodulos = dto.submodulos_ids ?? [];

    await this.validarModulosRaiz(modulosRaiz);
    await this.validarAddons(addons);
    await this.validarSubmodulosIncluidos(submodulos);

    const plan = await this.prisma.planes.create({
      data: {
        codigo: dto.codigo,
        descripcion: dto.descripcion,
        precio: dto.precio,
        tipo_ciclo: dto.tipo_ciclo || 'mensual',
        max_user: dto.max_user,
        max_producto: dto.max_producto,
        max_documentos: dto.max_documentos,
        periodo_prueba: dto.periodo_prueba || 0,
        max_ducumento_prueba: dto.max_documento_prueba || 0,
        max_clientes: dto.max_clientes || 0,
        monto_facturacion: dto.monto_facturacion || 0,
        dias_gracia: dto.dias_gracia || 0,
        max_empresas: dto.max_empresas || 0,
        max_sucursales: dto.max_sucursales || 0,
        tokens_ia_mensual: dto.tokens_ia_mensual ?? 0,
        active: dto.active ?? true,
        usuario: username || null,
      },
    });

    await this.syncModulosRaiz(plan.id, modulosRaiz);
    await this.syncSubmodulosIncluidos(plan.id, submodulos);
    await this.syncAddons(plan.id, addons);

    const result = await this.findOne(plan.id);
    this.logger.log(`Plan creado: ${plan.descripcion} (${plan.id})`, 'PlanesService');
    await this.auditService.log({
      empresa_id,
      user_id,
      action: 'CREATE',
      entity_type: 'plan',
      entity_id: plan.id,
      descripcion: `Plan creado: ${plan.descripcion} | Código: ${plan.codigo} | Precio: ${plan.precio}`,
      new_value: {
        codigo: plan.codigo,
        descripcion: plan.descripcion,
        precio: Number(plan.precio),
        tipo_ciclo: plan.tipo_ciclo,
        modulos_raiz: modulosRaiz.length,
        submodulos: submodulos.length,
        addons: addons.length,
      },
    });
    return result;
  }

  // ── Actualizar un plan existente ──
  async update(planId: string, dto: UpdatePlaneDto, username?: string, user_id?: string, empresa_id?: string) {
    const plan = await this.prisma.planes.findUnique({
      where: { id: planId },
    });
    if (!plan) throw new NotFoundException('Plan no encontrado');

    if (dto.codigo && dto.codigo !== plan.codigo) {
      const existente = await this.prisma.planes.findFirst({
        where: { codigo: dto.codigo, id: { not: planId } },
      });
      if (existente) {
        throw new BadRequestException(`Ya existe un plan con el código "${dto.codigo}"`);
      }
    }

    const modulosRaiz = dto.modulos_raiz_ids ?? dto.modulos;
    const addons = dto.addons;
    const submodulos = dto.submodulos_ids;

    if (modulosRaiz !== undefined) await this.validarModulosRaiz(modulosRaiz);
    if (addons !== undefined) await this.validarAddons(addons);
    if (submodulos !== undefined) await this.validarSubmodulosIncluidos(submodulos);

    await this.prisma.planes.update({
      where: { id: planId },
      data: {
        ...(dto.codigo !== undefined && { codigo: dto.codigo }),
        ...(dto.descripcion !== undefined && { descripcion: dto.descripcion }),
        ...(dto.precio !== undefined && { precio: dto.precio }),
        ...(dto.tipo_ciclo !== undefined && { tipo_ciclo: dto.tipo_ciclo }),
        ...(dto.max_user !== undefined && { max_user: dto.max_user }),
        ...(dto.max_producto !== undefined && { max_producto: dto.max_producto }),
        ...(dto.max_documentos !== undefined && { max_documentos: dto.max_documentos }),
        ...(dto.periodo_prueba !== undefined && { periodo_prueba: dto.periodo_prueba }),
        ...(dto.max_documento_prueba !== undefined && { max_ducumento_prueba: dto.max_documento_prueba }),
        ...(dto.max_clientes !== undefined && { max_clientes: dto.max_clientes }),
        ...(dto.monto_facturacion !== undefined && { monto_facturacion: dto.monto_facturacion }),
        ...(dto.dias_gracia !== undefined && { dias_gracia: dto.dias_gracia }),
        ...(dto.max_empresas !== undefined && { max_empresas: dto.max_empresas }),
        ...(dto.max_sucursales !== undefined && { max_sucursales: dto.max_sucursales }),
        ...(dto.tokens_ia_mensual !== undefined && { tokens_ia_mensual: dto.tokens_ia_mensual }),
        ...(dto.active !== undefined && { active: dto.active }),
        updated_at: new Date(),
        usuario: username || plan.usuario,
      },
    });

    let syncMod: SyncResult | undefined;
    let syncAdd: SyncResult | undefined;
    let syncSub: SyncResult | undefined;
    if (modulosRaiz !== undefined) syncMod = await this.syncModulosRaiz(planId, modulosRaiz);
    if (submodulos !== undefined) syncSub = await this.syncSubmodulosIncluidos(planId, submodulos);
    if (addons !== undefined) syncAdd = await this.syncAddons(planId, addons);

    const result = await this.findOne(planId);
    this.logger.log(`Plan actualizado: ${plan.descripcion} (${planId})`, 'PlanesService');
    await this.auditService.log({
      empresa_id,
      user_id,
      action: 'UPDATE',
      entity_type: 'plan',
      entity_id: planId,
      descripcion: `Plan actualizado: ${plan.descripcion} | Código: ${plan.codigo}`,
      old_value: {
        codigo: plan.codigo,
        descripcion: plan.descripcion,
        precio: Number(plan.precio),
        active: plan.active,
      },
      new_value: {
        ...(dto as Record<string, unknown>),
        sync_modulos_raiz: syncMod,
        sync_submodulos: syncSub,
        sync_addons: syncAdd,
      },
    });
    return result;
  }

  // ── Activar/Desactivar un plan ──
  async toggleActive(planId: string, user_id?: string, empresa_id?: string) {
    const plan = await this.prisma.planes.findUnique({
      where: { id: planId },
    });
    if (!plan) throw new NotFoundException('Plan no encontrado');

    if (plan.active) {
      const suscActivas = await this.prisma.suscripciones.count({
        where: { plan_id: planId, estado: 'Activo' },
      });
      if (suscActivas > 0) {
        throw new BadRequestException(`No se puede desactivar: el plan tiene ${suscActivas} suscripción(es) activa(s)`);
      }
    }

    const updated = await this.prisma.planes.update({
      where: { id: planId },
      data: { active: !plan.active, updated_at: new Date() },
    });

    const action = updated.active ? 'ACTIVATE' : 'DEACTIVATE';
    this.logger.log(`Plan (${planId}) ${updated.active ? 'activado' : 'desactivado'}`, 'PlanesService');
    await this.auditService.log({
      empresa_id,
      user_id,
      action,
      entity_type: 'plan',
      entity_id: planId,
      descripcion: `Plan ${updated.active ? 'activado' : 'desactivado'}: ${plan.descripcion}`,
      old_value: { active: plan.active },
      new_value: { active: updated.active },
    });
    return {
      message: `Plan ${updated.active ? 'activado' : 'desactivado'} correctamente`,
      data: { id: updated.id, active: updated.active },
    };
  }

  // ── Módulos disponibles (legacy, sin jerarquía) ──
  async getModulosDisponibles() {
    const modulos = await this.prisma.modulos.findMany({
      where: { active: true },
      orderBy: { codigo: 'asc' },
    });
    return {
      message: 'Módulos disponibles obtenidos correctamente',
      data: modulos,
    };
  }

  // ── Jerarquía completa: módulos raíz con sus submódulos (incluidos + addons) ──
  async getJerarquiaDisponible() {
    const modulos = await this.prisma.modulos.findMany({
      where: { active: true },
      orderBy: [{ orden: 'asc' }, { codigo: 'asc' }],
      include: {
        submodulos: {
          where: { active: true },
          orderBy: [{ orden: 'asc' }, { codigo: 'asc' }],
        },
      },
    });

    return {
      message: 'Jerarquía de módulos obtenida correctamente',
      data: modulos.map((m) => ({
        id: m.id,
        codigo: m.codigo,
        descripcion: m.descripcion,
        icono: m.icono,
        orden: m.orden,
        precio: m.precio,
        submodulos_incluidos: m.submodulos
          .filter((s) => !s.es_addon)
          .map((s) => ({
            id: s.id,
            codigo: s.codigo,
            descripcion: s.descripcion,
            orden: s.orden,
          })),
        addons: m.submodulos
          .filter((s) => s.es_addon)
          .map((s) => ({
            id: s.id,
            codigo: s.codigo,
            descripcion: s.descripcion,
            precio_default: s.precio_addon,
            orden: s.orden,
          })),
      })),
    };
  }

  // ── Estadísticas de planes ──
  async getEstadisticas() {
    const [total, activos, totalSuscripciones] = await Promise.all([
      this.prisma.planes.count(),
      this.prisma.planes.count({ where: { active: true } }),
      this.prisma.suscripciones.count({ where: { estado: 'Activo' } }),
    ]);

    const planPopular = await this.prisma.suscripciones.groupBy({
      by: ['plan_id'],
      where: { estado: 'Activo' },
      _count: { plan_id: true },
      orderBy: { _count: { plan_id: 'desc' } },
      take: 1,
    });

    let planMasUsado = null;
    if (planPopular.length > 0) {
      const p = await this.prisma.planes.findUnique({
        where: { id: planPopular[0].plan_id },
      });
      planMasUsado = p
        ? {
            id: p.id,
            descripcion: p.descripcion,
            suscripciones: planPopular[0]._count.plan_id,
          }
        : null;
    }

    return {
      data: {
        total_planes: total,
        planes_activos: activos,
        planes_inactivos: total - activos,
        total_suscripciones_activas: totalSuscripciones,
        plan_mas_usado: planMasUsado,
      },
    };
  }
}
