/**
 * ╔═══════════════════════════════════════════════════════════════╗
 * ║  IpWhitelistGuard — OT-Security Lite                          ║
 * ╚═══════════════════════════════════════════════════════════════╝
 *
 * Когда Feature Flag IP_WHITELIST = true,
 * пропускает только IP из списка config.ipWhitelist.
 *
 * Формат ipWhitelist в config.json:
 *   "ipWhitelist": ["192.168.1.0", "10.0.0.0/8"]
 * Поддерживается: точный IP и префикс (первые N октетов).
 *
 * Важно: подключать ПОСЛЕ JwtAuthGuard чтобы IP-ban не влиял на
 * публичные эндпоинты (/api/health) — для них Guard не применяется.
 */

import {
  Injectable, CanActivate, ExecutionContext, ForbiddenException,
} from '@nestjs/common';
import { FeatureFlagsService } from '../../config/feature-flags.service';
import { AppConfigService }    from '../../modules/config/app-config.service';

@Injectable()
export class IpWhitelistGuard implements CanActivate {
  constructor(
    private readonly features: FeatureFlagsService,
    private readonly appCfg:   AppConfigService,
  ) {}

  canActivate(ctx: ExecutionContext): boolean {
    // Если флаг выключен — пропускаем всех
    if (!this.features.isEnabled('IP_WHITELIST')) return true;

    const request  = ctx.switchToHttp().getRequest();
    const clientIp = this.extractIp(request);
    const whitelist: string[] = this.appCfg.get('ipWhitelist') ?? [];

    // Пустой whitelist = пропускаем всех (безопасный дефолт)
    if (!whitelist.length) return true;

    const allowed = whitelist.some(entry => this.matchIp(clientIp, entry));
    if (!allowed) {
      throw new ForbiddenException(`Доступ запрещён: IP ${clientIp} не в белом списке`);
    }

    return true;
  }

  /** Извлекаем реальный IP (учитываем X-Forwarded-For за Nginx) */
  private extractIp(req: any): string {
    const forwarded = req.headers?.['x-forwarded-for'];
    if (forwarded) return String(forwarded).split(',')[0].trim();
    return req.socket?.remoteAddress ?? req.ip ?? '0.0.0.0';
  }

  /**
   * Проверка соответствия IP записи в whitelist.
   * Поддерживает:
   *  - точный IP:   "192.168.1.10"
   *  - префикс:     "192.168.1." (все адреса подсети)
   *  - подсеть /N:  "10.0.0.0/8" (упрощённая проверка)
   */
  private matchIp(clientIp: string, entry: string): boolean {
    if (clientIp === entry) return true;
    if (clientIp === '::1' && entry === '127.0.0.1') return true;

    // Префикс вида "192.168.1."
    if (entry.endsWith('.') && clientIp.startsWith(entry)) return true;

    // CIDR /N (упрощённо — проверяем первые N/8 октетов)
    if (entry.includes('/')) {
      const [base, bits] = entry.split('/');
      const octets = Math.floor(parseInt(bits) / 8);
      const prefix = base.split('.').slice(0, octets).join('.');
      const clientPrefix = clientIp.split('.').slice(0, octets).join('.');
      if (prefix === clientPrefix) return true;
    }

    return false;
  }
}
