import {
  Injectable,
  CanActivate,
  ExecutionContext,
  ForbiddenException,
  Inject,
  Logger,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { PlanFeature } from '@domain/value-objects/plan-feature.vo';
import {
  SubscriptionRepository,
  SUBSCRIPTION_REPOSITORY,
} from '@domain/ports/out/subscription.repository';
import { REQUIRED_FEATURES_KEY } from '../decorators/requires-feature.decorator';

/**
 * Feature Flag Guard
 *
 * Checks if the user's subscription plan includes the required features.
 * First tries to read plan from JWT payload (fast path), falls back to DB lookup.
 *
 * Usage:
 * @UseGuards(JwtAuthGuard, RolesGuard, FeatureFlagGuard)
 * @RequiresFeature('dashboard')
 */
@Injectable()
export class FeatureFlagGuard implements CanActivate {
  private readonly logger = new Logger(FeatureFlagGuard.name);

  constructor(
    private readonly reflector: Reflector,
    @Inject(SUBSCRIPTION_REPOSITORY)
    private readonly subscriptionRepository: SubscriptionRepository
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    // Get required features from @RequiresFeature() decorator
    const requiredFeatures = this.reflector.getAllAndOverride<PlanFeature[]>(
      REQUIRED_FEATURES_KEY,
      [context.getHandler(), context.getClass()]
    );

    // If no features are required, allow access
    if (!requiredFeatures || requiredFeatures.length === 0) {
      return true;
    }

    const request = context.switchToHttp().getRequest();
    const user = request.user;

    if (!user || !user.organizationId) {
      return false;
    }

    // ADMIN users have full access to all features — no plan check needed
    if (user.role === 'ADMIN') {
      return true;
    }

    // Fast path: check plan features from JWT payload
    if (user.planFeatures && Array.isArray(user.planFeatures)) {
      const hasAllFeatures = requiredFeatures.every(feature => user.planFeatures.includes(feature));

      if (hasAllFeatures) {
        return true;
      }

      // JWT says no — but JWT might be stale after an upgrade.
      // Fall through to DB check.
    }

    // Slow path: DB lookup for fresh subscription data
    try {
      const subscription = await this.subscriptionRepository.findByOrganizationId(
        user.organizationId
      );

      if (!subscription) {
        // No subscription means Bronze (free) plan — no premium features
        this.throwFeatureRequired(requiredFeatures);
      }

      const plan = subscription!.plan;
      const missingFeatures = requiredFeatures.filter(feature => !plan.hasFeature(feature));

      if (missingFeatures.length > 0) {
        this.throwFeatureRequired(requiredFeatures);
      }

      return true;
    } catch (error) {
      if (error instanceof ForbiddenException) {
        throw error;
      }
      this.logger.error(`Failed to check subscription features: ${error}`);
      // On DB error, deny access to premium features rather than 500
      this.throwFeatureRequired(requiredFeatures);
    }
  }

  private throwFeatureRequired(features: PlanFeature[]): never {
    const featureNames = features.join(', ');
    throw new ForbiddenException(
      `Cette fonctionnalité nécessite un plan supérieur. Fonctionnalités requises : ${featureNames}. Passez à un plan Silver ou supérieur.`
    );
  }
}
