from functools import wraps
from flask import session, jsonify, request
from typing import Callable, Optional, Any
import time
import hashlib


def auth_required(func: Callable):
    """Require authentication for this route"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        if 'user_id' not in session:
            return jsonify({'error': 'Authentication required'}), 401
        return func(*args, **kwargs)
    return wrapper


def admin_required(func: Callable):
    """Require admin privileges for this route"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        if 'user_id' not in session:
            return jsonify({'error': 'Authentication required'}), 401
        if not session.get('is_admin', False):
            return jsonify({'error': 'Admin privileges required'}), 403
        return func(*args, **kwargs)
    return wrapper


def rate_limit(max_requests: int = 100, window: int = 60):
    """Rate limit decorator (max_requests per window seconds)"""
    requests_cache = {}
    
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            client_id = request.remote_addr
            current_time = time.time()
            
            if client_id not in requests_cache:
                requests_cache[client_id] = []
            
            requests_cache[client_id] = [
                req_time for req_time in requests_cache[client_id]
                if current_time - req_time < window
            ]
            
            if len(requests_cache[client_id]) >= max_requests:
                return jsonify({
                    'error': 'Rate limit exceeded',
                    'retry_after': window
                }), 429
            
            requests_cache[client_id].append(current_time)
            return func(*args, **kwargs)
        
        return wrapper
    return decorator


_cache = {}


def cache(ttl: int = 300):
    """Cache decorator (ttl in seconds)"""
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            cache_key = hashlib.md5(
                f"{func.__name__}:{args}:{kwargs}".encode()
            ).hexdigest()
            
            current_time = time.time()
            
            if cache_key in _cache:
                cached_time, cached_value = _cache[cache_key]
                if current_time - cached_time < ttl:
                    return cached_value
            
            result = func(*args, **kwargs)
            _cache[cache_key] = (current_time, result)
            return result
        
        return wrapper
    return decorator


def validate_schema(schema_class):
    """Validate request data against a Pydantic schema"""
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                from pydantic import ValidationError
                
                if request.is_json:
                    data = request.get_json()
                else:
                    data = request.form.to_dict()
                
                validated = schema_class(**data)
                kwargs['validated_data'] = validated
                return func(*args, **kwargs)
            except ValidationError as e:
                return jsonify({
                    'error': 'Validation failed',
                    'details': e.errors()
                }), 400
        
        return wrapper
    return decorator
