import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import {
  AI_DOMAINS,
  AI_DOMAIN_LIST,
  AIDomain,
  AIDomainMapping,
} from './ai-domains.config';

/**
 * Centraliza la resolución de "qué dominios AI tiene activos esta empresa".
 *
 * Cada bloque del AI Dashboard (insights, alerts, timeline, predictions, chat)
 * llama a `getActiveDomains(empresaId)` y filtra su salida.
 *
 * Holdings/resellers no tienen suscripción propia → asumimos TODOS los dominios.
 *
 * Cache liviano en memoria (60s) para no martillar la DB.
 */

interface CacheEntry {
  value: AIDomain[];
  expiresAt: number;
}

const TTL_MS = 60_000;

@Injectable()
export class ModulesContextService {
  private readonly logger = new Logger(ModulesContextService.name);
  private readonly cache = new Map<string, CacheEntry>();

  constructor(private readonly prisma: PrismaService) {}

  /** Lista de dominios AI activos para la empresa. */
  async getActiveDomains(empresaId: string): Promise<AIDomain[]> {
    const hit = this.cache.get(empresaId);
    const now = Date.now();
    if (hit && hit.expiresAt > now) return hit.value;

    const value = await this._compute(empresaId).catch((e) => {
      this.logger.warn(`getActiveDomains fallo empresa=${empresaId}: ${e.message}`);
      return [] as AIDomain[];
    });

    this.cache.set(empresaId, { value, expiresAt: now + TTL_MS });
    return value;
  }

  /** Atajo: ¿la empresa tiene el dominio X activo? */
  async tieneDominio(empresaId: string, dominio: AIDomain): Promise<boolean> {
    const active = await this.getActiveDomains(empresaId);
    return active.includes(dominio);
  }

  /** Invalida el cache (usar al cambiar suscripción). */
  invalidate(empresaId: string) {
    this.cache.delete(empresaId);
  }

  private async _compute(empresaId: string): Promise<AIDomain[]> {
    // Holdings/resellers ven todo.
    const empresa = await this.prisma.empresas.findUnique({
      where: { id: empresaId },
      select: { type: true },
    });
    if (empresa?.type === 'holding' || empresa?.type === 'reseller') {
      return [...AI_DOMAIN_LIST];
    }

    const suscripcion = await this.prisma.suscripciones.findFirst({
      where: { empresa_id: empresaId, estado: { in: ['Activo', 'EnGracia'] } },
      orderBy: { created_at: 'desc' },
      select: { id: true },
    });
    if (!suscripcion) return [];

    // Set unificado de códigos contratados: módulos raíz + submódulos
    // efectivamente activos (incluidos + addons contratados, excluyendo
    // los marcados como excluidos en suscripcion_submodulos).
    const [modulosRaiz, submodulosIncluidos, submodulosExcluidos, addonsActivos] =
      await Promise.all([
        this.prisma.suscripcion_modulos.findMany({
          where: {
            suscripcion_id: suscripcion.id,
            activo: true,
            modulos: { active: true },
          },
          select: {
            modulos: {
              select: {
                codigo: true,
                submodulos: {
                  where: { active: true, es_addon: false },
                  select: { id: true, codigo: true },
                },
              },
            },
          },
        }),
        Promise.resolve(null), // placeholder mantiene índice
        this.prisma.suscripcion_submodulos.findMany({
          where: {
            suscripcion_id: suscripcion.id,
            active: false,
            submodulos: { es_addon: false },
          },
          select: { submodulo_id: true },
        }),
        this.prisma.suscripcion_submodulos.findMany({
          where: {
            suscripcion_id: suscripcion.id,
            active: true,
            submodulos: { active: true, es_addon: true },
          },
          select: { submodulos: { select: { codigo: true } } },
        }),
      ]);

    void submodulosIncluidos; // descartado, lo derivamos de modulosRaiz
    const excluidosIds = new Set(submodulosExcluidos.map((e) => e.submodulo_id));
    const activos = new Set<string>();

    for (const sm of modulosRaiz) {
      if (sm.modulos?.codigo) activos.add(sm.modulos.codigo);
      for (const sub of sm.modulos?.submodulos ?? []) {
        if (!excluidosIds.has(sub.id)) activos.add(sub.codigo);
      }
    }
    for (const a of addonsActivos) {
      if (a.submodulos?.codigo) activos.add(a.submodulos.codigo);
    }

    return AI_DOMAIN_LIST.filter((dominio) => {
      const mapping = AI_DOMAINS[dominio] as AIDomainMapping;
      const enModulos = (mapping.modulos ?? []).some((c) => activos.has(c));
      const enSubmodulos = (mapping.submodulos ?? []).some((c) =>
        activos.has(c),
      );
      return enModulos || enSubmodulos;
    });
  }
}
