"""
Custom throttling classes for API rate limiting
"""
import logging
import ipaddress
from rest_framework.throttling import AnonRateThrottle, UserRateThrottle

# Logger yapılandırması
logger = logging.getLogger('throttling')
logger.setLevel(logging.INFO)

# File handler ekle (logs dizinine yaz)
import os
import sys
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent
LOG_DIR = BASE_DIR / 'logs'
LOG_DIR.mkdir(exist_ok=True)

# Formatter oluştur
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# File handler oluştur (dosyaya yaz)
file_handler = logging.FileHandler(LOG_DIR / 'throttling.log', encoding='utf-8')
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)

# Stream handler oluştur (konsola yaz - Docker için)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)

# Handler'ları logger'a ekle (duplicate handler'ları önlemek için)
if not logger.handlers:
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)


def is_cloudflare_ip(ip):
    """
    IP adresinin Cloudflare IP aralığında olup olmadığını kontrol eder
    Cloudflare IP aralıkları: https://www.cloudflare.com/ips/
    """
    try:
        ip_obj = ipaddress.ip_address(ip)
        # Cloudflare IPv4 aralıkları (en yaygın olanlar)
        cloudflare_ranges = [
            ipaddress.ip_network('173.245.48.0/20'),
            ipaddress.ip_network('103.21.244.0/22'),
            ipaddress.ip_network('103.22.200.0/22'),
            ipaddress.ip_network('103.31.4.0/22'),
            ipaddress.ip_network('141.101.64.0/18'),
            ipaddress.ip_network('108.162.192.0/18'),
            ipaddress.ip_network('190.93.240.0/20'),
            ipaddress.ip_network('188.114.96.0/20'),
            ipaddress.ip_network('197.234.240.0/22'),
            ipaddress.ip_network('198.41.128.0/17'),
            ipaddress.ip_network('162.158.0.0/15'),
            ipaddress.ip_network('104.16.0.0/13'),
            ipaddress.ip_network('104.24.0.0/14'),
            ipaddress.ip_network('172.64.0.0/13'),
            ipaddress.ip_network('131.0.72.0/22'),
        ]
        # Cloudflare IPv6 aralıkları
        cloudflare_ranges_v6 = [
            ipaddress.ip_network('2400:cb00::/32'),
            ipaddress.ip_network('2606:4700::/32'),
            ipaddress.ip_network('2803:f800::/32'),
            ipaddress.ip_network('2405:b500::/32'),
            ipaddress.ip_network('2405:8100::/32'),
            ipaddress.ip_network('2a06:98c0::/29'),
            ipaddress.ip_network('2c0f:f248::/32'),
        ]
        
        all_ranges = cloudflare_ranges + cloudflare_ranges_v6
        return any(ip_obj in network for network in all_ranges)
    except (ValueError, ipaddress.AddressValueError):
        return False


class CustomAnonRateThrottle(AnonRateThrottle):
    """
    Belirli IP'ler için throttling'i bypass eder
    """
    EXEMPT_IPS = [
        '127.0.0.1',
        'localhost',
        '::1',
        '212.64.215.243',
        '162.158.210.254',
        '188.132.232.119',
    ]

    def get_client_ip(self, request):
        """
        Client IP adresini al
        Cloudflare kullanıldığında CF-Connecting-IP header'ını öncelikli kullanır
        """
        # Cloudflare gerçek client IP'si (en güvenilir)
        cf_connecting_ip = request.META.get('HTTP_CF_CONNECTING_IP')
        if cf_connecting_ip:
            return cf_connecting_ip.strip()
        
        # True-Client-IP (bazı Cloudflare yapılandırmalarında)
        true_client_ip = request.META.get('HTTP_TRUE_CLIENT_IP')
        if true_client_ip:
            return true_client_ip.strip()
        
        # X-Forwarded-For (fallback)
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            # İlk IP genellikle gerçek client IP'sidir
            ip = x_forwarded_for.split(',')[0].strip()
            return ip
        
        # Son çare: REMOTE_ADDR
        remote_addr = request.META.get('REMOTE_ADDR', 'unknown')
        
        # Eğer REMOTE_ADDR Cloudflare IP'si ise, gerçek IP bulunamadı demektir
        if is_cloudflare_ip(remote_addr):
            logger.warning(
                f"[IP DETECTION] Cloudflare IP tespit edildi ({remote_addr}) "
                f"ama gerçek client IP bulunamadı. CF-Connecting-IP header'ı eksik olabilir."
            )
        
        return remote_addr

    def get_cache_key(self, request, view):
        """
        Cache key'i gerçek client IP'ye göre oluşturur
        Cloudflare kullanıldığında gerçek IP'yi kullanır
        """
        # Gerçek client IP'yi al
        ident = self.get_client_ip(request)
        
        # Cache key format: throttle_anon_{ip}
        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }

    def allow_request(self, request, view):
        # Get client IP
        ip = self.get_client_ip(request)
        remote_addr = request.META.get('REMOTE_ADDR', 'unknown')
        host = request.get_host().split(':')[0]
        path = request.path
        method = request.method
        user_agent = request.META.get('HTTP_USER_AGENT', 'unknown')
        
        # View bilgisi
        view_name = getattr(view, '__class__', None)
        view_name = view_name.__name__ if view_name else 'unknown'
        
        # Cloudflare kontrolü
        is_from_cloudflare = is_cloudflare_ip(remote_addr)
        cf_info = f"CF-IP: {remote_addr}" if is_from_cloudflare else ""
        
        # Belirtilen IP'lerden geliyorsa throttling yapma
        if ip in self.EXEMPT_IPS:
            logger.info(
                f"[ANON THROTTLE - BYPASS] Real-IP: {ip} | {cf_info} | Host: {host} | "
                f"Path: {path} | Method: {method} | View: {view_name} | "
                f"User-Agent: {user_agent[:100]}"
            )
            return True

        # Normal throttling kurallarını uygula
        allowed = super().allow_request(request, view)
        
        # Throttle durumunu kontrol et
        if allowed:
            # Rate limit bilgilerini al
            throttle_scope = getattr(view, 'throttle_scope', None) or 'anon'
            rate = self.get_rate()
            num_requests, duration = self.parse_rate(rate)
            
            # Cache key'den kalan istek sayısını tahmin et
            cache_key = self.get_cache_key(request, view)
            history = self.cache.get(cache_key, [])
            remaining = max(0, num_requests - len(history))
            
            logger.info(
                f"[ANON THROTTLE - ALLOWED] Real-IP: {ip} | {cf_info} | Host: {host} | "
                f"Path: {path} | Method: {method} | View: {view_name} | "
                f"Rate: {rate} | Remaining: {remaining}/{num_requests} | "
                f"User-Agent: {user_agent[:100]}"
            )
        else:
            # Throttle limit aşıldı
            rate = self.get_rate()
            num_requests, duration = self.parse_rate(rate)
            
            logger.warning(
                f"[ANON THROTTLE - BLOCKED] Real-IP: {ip} | {cf_info} | Host: {host} | "
                f"Path: {path} | Method: {method} | View: {view_name} | "
                f"Rate: {rate} | Limit: {num_requests}/{duration} | "
                f"User-Agent: {user_agent[:100]}"
            )
        
        return allowed


class CustomUserRateThrottle(UserRateThrottle):
    """
    Belirli kullanıcılar veya domainler için throttling'i bypass eder
    """
    EXEMPT_HOSTS = [
        'api.denizogur.com.tr',
        'back.beyhan.gen.tr',
        'shop.beyhan.gen.tr',
        'beyhan.gen.tr',
        'denizogur.com.tr',
        'denizour.com.tr',
        'localhost',
        '127.0.0.1',
    ]

    def get_client_ip(self, request):
        """
        Client IP adresini al
        Cloudflare kullanıldığında CF-Connecting-IP header'ını öncelikli kullanır
        """
        # Cloudflare gerçek client IP'si (en güvenilir)
        cf_connecting_ip = request.META.get('HTTP_CF_CONNECTING_IP')
        if cf_connecting_ip:
            return cf_connecting_ip.strip()
        
        # True-Client-IP (bazı Cloudflare yapılandırmalarında)
        true_client_ip = request.META.get('HTTP_TRUE_CLIENT_IP')
        if true_client_ip:
            return true_client_ip.strip()
        
        # X-Forwarded-For (fallback)
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            # İlk IP genellikle gerçek client IP'sidir
            ip = x_forwarded_for.split(',')[0].strip()
            return ip
        
        # Son çare: REMOTE_ADDR
        remote_addr = request.META.get('REMOTE_ADDR', 'unknown')
        
        # Eğer REMOTE_ADDR Cloudflare IP'si ise, gerçek IP bulunamadı demektir
        if is_cloudflare_ip(remote_addr):
            logger.warning(
                f"[IP DETECTION] Cloudflare IP tespit edildi ({remote_addr}) "
                f"ama gerçek client IP bulunamadı. CF-Connecting-IP header'ı eksik olabilir."
            )
        
        return remote_addr

    def get_cache_key(self, request, view):
        """
        Cache key'i gerçek client IP'ye göre oluşturur
        Cloudflare kullanıldığında gerçek IP'yi kullanır
        UserRateThrottle için user ID de eklenir
        """
        # Authenticated kullanıcı için user ID kullan
        if request.user and request.user.is_authenticated:
            ident = request.user.pk
        else:
            # Anonymous kullanıcı için gerçek client IP'yi kullan
            ident = self.get_client_ip(request)
        
        # Cache key format: throttle_user_{user_id} veya throttle_anon_{ip}
        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }

    def allow_request(self, request, view):
        # Get client IP
        ip = self.get_client_ip(request)
        remote_addr = request.META.get('REMOTE_ADDR', 'unknown')
        host = request.get_host().split(':')[0]
        path = request.path
        method = request.method
        user_agent = request.META.get('HTTP_USER_AGENT', 'unknown')
        
        # View bilgisi
        view_name = getattr(view, '__class__', None)
        view_name = view_name.__name__ if view_name else 'unknown'
        
        # User bilgisi
        user_info = 'anonymous'
        if request.user and request.user.is_authenticated:
            user_info = f"user_id:{request.user.id} | email:{getattr(request.user, 'email', 'N/A')} | staff:{request.user.is_staff}"

        # Cloudflare kontrolü
        is_from_cloudflare = is_cloudflare_ip(remote_addr)
        cf_info = f"CF-IP: {remote_addr}" if is_from_cloudflare else ""

        # Host kontrolü
        if host in self.EXEMPT_HOSTS:
            logger.info(
                f"[USER THROTTLE - BYPASS (HOST)] Real-IP: {ip} | {cf_info} | Host: {host} | "
                f"Path: {path} | Method: {method} | View: {view_name} | "
                f"User: {user_info} | User-Agent: {user_agent[:100]}"
            )
            return True

        # Authenticated kullanıcı için throttling yapma (staff users)
        if request.user and request.user.is_authenticated and request.user.is_staff:
            logger.info(
                f"[USER THROTTLE - BYPASS (STAFF)] Real-IP: {ip} | {cf_info} | Host: {host} | "
                f"Path: {path} | Method: {method} | View: {view_name} | "
                f"User: {user_info} | User-Agent: {user_agent[:100]}"
            )
            return True

        # Normal throttling kurallarını uygula
        allowed = super().allow_request(request, view)
        
        # Throttle durumunu kontrol et
        if allowed:
            # Rate limit bilgilerini al
            throttle_scope = getattr(view, 'throttle_scope', None) or 'user'
            rate = self.get_rate()
            num_requests, duration = self.parse_rate(rate)
            
            # Cache key'den kalan istek sayısını tahmin et
            cache_key = self.get_cache_key(request, view)
            history = self.cache.get(cache_key, [])
            remaining = max(0, num_requests - len(history))
            
            logger.info(
                f"[USER THROTTLE - ALLOWED] Real-IP: {ip} | {cf_info} | Host: {host} | "
                f"Path: {path} | Method: {method} | View: {view_name} | "
                f"User: {user_info} | Rate: {rate} | Remaining: {remaining}/{num_requests} | "
                f"User-Agent: {user_agent[:100]}"
            )
        else:
            # Throttle limit aşıldı
            rate = self.get_rate()
            num_requests, duration = self.parse_rate(rate)
            
            logger.warning(
                f"[USER THROTTLE - BLOCKED] Real-IP: {ip} | {cf_info} | Host: {host} | "
                f"Path: {path} | Method: {method} | View: {view_name} | "
                f"User: {user_info} | Rate: {rate} | Limit: {num_requests}/{duration} | "
                f"User-Agent: {user_agent[:100]}"
            )
        
        return allowed

