/**
 * FEATURE FLAGS SERVICE - REFERENCE IMPLEMENTATION
 *
 * @description Servicio de feature flags para activación/desactivación
 * de funcionalidades en runtime.
 *
 * @usage
 * ```typescript
 * if (await this.featureFlags.isEnabled('new-dashboard', userId)) {
 *   return this.newDashboard();
 * }
 * return this.legacyDashboard();
 * ```
 *
 * @origin gamilit/apps/backend/src/modules/admin/services/feature-flags.service.ts
 */

import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

// Adaptar imports según proyecto
// import { FeatureFlag, FeatureFlagOverride } from '../entities';

/**
 * Estrategias de rollout
 */
export enum RolloutStrategyEnum {
  /** Todos los usuarios */
  ALL = 'all',
  /** Ningún usuario */
  NONE = 'none',
  /** Porcentaje de usuarios */
  PERCENTAGE = 'percentage',
  /** Lista de usuarios específicos */
  USER_LIST = 'user_list',
  /** Por rol */
  ROLE = 'role',
  /** Por tenant */
  TENANT = 'tenant',
}

@Injectable()
export class FeatureFlagsService {
  private readonly logger = new Logger(FeatureFlagsService.name);
  private cache = new Map<string, FeatureFlag>();
  private cacheExpiry = 0;
  private readonly CACHE_TTL = 60000; // 1 minuto

  constructor(
    @InjectRepository(FeatureFlag, 'config')
    private readonly flagRepo: Repository<FeatureFlag>,

    @InjectRepository(FeatureFlagOverride, 'config')
    private readonly overrideRepo: Repository<FeatureFlagOverride>,
  ) {}

  /**
   * Verificar si una feature está habilitada
   *
   * @param flagKey - Clave única del feature flag
   * @param context - Contexto del usuario para evaluación
   */
  async isEnabled(flagKey: string, context?: FeatureFlagContext): Promise<boolean> {
    // 1. Verificar override específico del usuario
    if (context?.userId) {
      const override = await this.getUserOverride(flagKey, context.userId);
      if (override !== null) {
        return override;
      }
    }

    // 2. Obtener flag (con cache)
    const flag = await this.getFlag(flagKey);
    if (!flag) {
      this.logger.warn(`Feature flag not found: ${flagKey}`);
      return false;
    }

    // 3. Evaluar según estrategia
    return this.evaluateStrategy(flag, context);
  }

  /**
   * Obtener todos los flags con su estado para un usuario
   */
  async getAllFlags(context?: FeatureFlagContext): Promise<Record<string, boolean>> {
    await this.refreshCacheIfNeeded();

    const result: Record<string, boolean> = {};
    for (const [key, flag] of this.cache) {
      result[key] = await this.isEnabled(key, context);
    }

    return result;
  }

  /**
   * Crear o actualizar un feature flag
   */
  async upsertFlag(dto: UpsertFeatureFlagDto): Promise<FeatureFlag> {
    let flag = await this.flagRepo.findOne({ where: { key: dto.key } });

    if (flag) {
      Object.assign(flag, dto);
    } else {
      flag = this.flagRepo.create(dto);
    }

    const saved = await this.flagRepo.save(flag);
    this.invalidateCache();
    return saved;
  }

  /**
   * Establecer override para un usuario específico
   */
  async setUserOverride(
    flagKey: string,
    userId: string,
    enabled: boolean,
  ): Promise<void> {
    await this.overrideRepo.upsert(
      { flag_key: flagKey, user_id: userId, enabled },
      ['flag_key', 'user_id'],
    );
  }

  /**
   * Eliminar override de usuario
   */
  async removeUserOverride(flagKey: string, userId: string): Promise<void> {
    await this.overrideRepo.delete({ flag_key: flagKey, user_id: userId });
  }

  // ============ HELPERS PRIVADOS ============

  private async getFlag(key: string): Promise<FeatureFlag | null> {
    await this.refreshCacheIfNeeded();
    return this.cache.get(key) || null;
  }

  private async getUserOverride(flagKey: string, userId: string): Promise<boolean | null> {
    const override = await this.overrideRepo.findOne({
      where: { flag_key: flagKey, user_id: userId },
    });
    return override?.enabled ?? null;
  }

  private evaluateStrategy(flag: FeatureFlag, context?: FeatureFlagContext): boolean {
    if (!flag.is_active) return false;

    switch (flag.strategy) {
      case RolloutStrategyEnum.ALL:
        return true;

      case RolloutStrategyEnum.NONE:
        return false;

      case RolloutStrategyEnum.PERCENTAGE:
        if (!context?.userId) return false;
        // Hash consistente basado en userId
        const hash = this.hashUserId(context.userId, flag.key);
        return hash < (flag.percentage || 0);

      case RolloutStrategyEnum.USER_LIST:
        if (!context?.userId) return false;
        return (flag.user_list || []).includes(context.userId);

      case RolloutStrategyEnum.ROLE:
        if (!context?.role) return false;
        return (flag.allowed_roles || []).includes(context.role);

      case RolloutStrategyEnum.TENANT:
        if (!context?.tenantId) return false;
        return (flag.allowed_tenants || []).includes(context.tenantId);

      default:
        return false;
    }
  }

  private hashUserId(userId: string, flagKey: string): number {
    // Hash simple para distribución consistente
    const str = `${userId}-${flagKey}`;
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      hash = ((hash << 5) - hash) + str.charCodeAt(i);
      hash |= 0;
    }
    return Math.abs(hash % 100);
  }

  private async refreshCacheIfNeeded(): Promise<void> {
    if (Date.now() > this.cacheExpiry) {
      const flags = await this.flagRepo.find();
      this.cache.clear();
      for (const flag of flags) {
        this.cache.set(flag.key, flag);
      }
      this.cacheExpiry = Date.now() + this.CACHE_TTL;
    }
  }

  private invalidateCache(): void {
    this.cacheExpiry = 0;
  }
}

// ============ TIPOS ============

interface FeatureFlagContext {
  userId?: string;
  role?: string;
  tenantId?: string;
}

interface FeatureFlag {
  id: string;
  key: string;
  name: string;
  description?: string;
  is_active: boolean;
  strategy: RolloutStrategyEnum;
  percentage?: number;
  user_list?: string[];
  allowed_roles?: string[];
  allowed_tenants?: string[];
}

interface FeatureFlagOverride {
  flag_key: string;
  user_id: string;
  enabled: boolean;
}

interface UpsertFeatureFlagDto {
  key: string;
  name: string;
  description?: string;
  is_active?: boolean;
  strategy?: RolloutStrategyEnum;
  percentage?: number;
  user_list?: string[];
  allowed_roles?: string[];
  allowed_tenants?: string[];
}
