import {
  CanActivate,
  ExecutionContext,
  ForbiddenException,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import {
  REQUIRED_FEATURES_KEY,
  REQUIRED_PLANS_KEY,
  SKIP_TENANT_CHECK_KEY,
} from './tenant.decorator.js';
import { TenantRequest, TenantPlan, FeatureConfig } from './tenant.types.js';

/**
 * 租户守卫
 * 确保请求具有有效的租户上下文，并验证功能和套餐权限
 */
@Injectable()
export class TenantGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    // 检查是否跳过租户检查
    const skipCheck = this.reflector.getAllAndOverride<boolean>(SKIP_TENANT_CHECK_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (skipCheck) {
      return true;
    }

    const request = context.switchToHttp().getRequest<TenantRequest>();

    // 验证租户上下文存在
    if (!request.tenant || !request.tenantId) {
      throw new UnauthorizedException('Tenant context required');
    }

    // 验证租户状态
    if (request.tenant.status !== 'ACTIVE') {
      throw new ForbiddenException(`Tenant is ${request.tenant.status.toLowerCase()}`);
    }

    // 验证所需功能
    const requiredFeatures = this.reflector.getAllAndOverride<string[]>(REQUIRED_FEATURES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (requiredFeatures?.length) {
      const features = request.tenant.config.features || {};
      for (const feature of requiredFeatures) {
        if (!features[feature as keyof FeatureConfig]) {
          throw new ForbiddenException(`Feature '${feature}' is not enabled for this tenant`);
        }
      }
    }

    // 验证所需套餐
    const requiredPlans = this.reflector.getAllAndOverride<string[]>(REQUIRED_PLANS_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (requiredPlans?.length) {
      const planHierarchy: Record<TenantPlan, number> = {
        [TenantPlan.FREE]: 0,
        [TenantPlan.STANDARD]: 1,
        [TenantPlan.ENTERPRISE]: 2,
      };

      const tenantPlanLevel = planHierarchy[request.tenant.plan as TenantPlan] ?? 0;
      const minRequiredLevel = Math.min(
        ...requiredPlans.map((p: string) => planHierarchy[p as TenantPlan] ?? 999),
      );

      if (tenantPlanLevel < minRequiredLevel) {
        throw new ForbiddenException(
          `This feature requires ${requiredPlans.join(' or ')} plan`,
        );
      }
    }

    return true;
  }
}
