"""Decorators for transform operators to ensure safety and collect metrics."""

import functools
import logging
import time
from typing import Any, Callable, Dict

log = logging.getLogger(__name__)


def safe_transform(func: Callable) -> Callable:
    """Decorator to safely handle transform errors and provide consistent error responses."""

    @functools.wraps(func)
    def wrapper(value: Any, **kwargs) -> Dict[str, Any]:
        try:
            # Input validation
            if value is None:
                return {
                    "value": None,
                    "error": "null_input",
                    "meta": {"op": func.__name__},
                }

            # Call the actual transform
            result = func(value, **kwargs)

            # Ensure result is a dict
            if not isinstance(result, dict):
                return {"value": result, "meta": {"op": func.__name__}}

            return result

        except Exception as e:
            log.error(f"Transform {func.__name__} failed: {e}", exc_info=True)
            return {
                "value": None,
                "error": str(e),
                "error_type": type(e).__name__,
                "meta": {"op": func.__name__},
            }

    return wrapper


def with_metrics(func: Callable) -> Callable:
    """Decorator to collect metrics about transform execution."""

    @functools.wraps(func)
    def wrapper(*args, **kwargs) -> Any:
        start_time = time.perf_counter()
        error_count = 0
        rows_processed = 0

        try:
            # Try to determine row count from args
            if args and isinstance(args[0], (list, tuple)):
                rows_processed = len(args[0])
            elif args and isinstance(args[0], dict):
                # For row-based operations
                row = args[0].get("row")
                if row:
                    rows_processed = 1

            result = func(*args, **kwargs)

        except Exception:
            error_count = 1
            raise
        finally:
            # Calculate duration
            duration_ms = int((time.perf_counter() - start_time) * 1000)

            # Emit structured log
            log.info(
                "transform_metrics",
                extra={
                    "op_name": func.__name__,
                    "rows": rows_processed,
                    "duration_ms": duration_ms,
                    "error_count": error_count,
                },
            )

        return result

    return wrapper


def batch_safe(func: Callable) -> Callable:
    """Decorator for batch operations to handle individual row failures gracefully."""

    @functools.wraps(func)
    def wrapper(values: Any, **kwargs) -> Any:
        if not isinstance(values, (list, tuple)):
            # Single value, use safe_transform behavior
            return safe_transform(func)(values, **kwargs)

        results = []
        errors = []

        for idx, value in enumerate(values):
            try:
                result = func(value, **kwargs)
                results.append(result)
            except Exception as e:
                log.warning(f"Row {idx} failed in {func.__name__}: {e}")
                errors.append({"row": idx, "error": str(e)})
                results.append({"value": None, "error": str(e)})

        # Add error summary to metadata if any errors occurred
        if errors and results:
            return {
                "values": results,
                "meta": {
                    "op": func.__name__,
                    "total_rows": len(values),
                    "error_count": len(errors),
                    "errors": errors[:10],  # Limit to first 10 errors
                },
            }

        return results

    return wrapper


def cached_transform(ttl: int = 3600):
    """Decorator to cache transform results with TTL (time to live in seconds)."""
    import hashlib
    import json

    def decorator(func: Callable) -> Callable:
        # Use LRU cache with maxsize
        cache = {}
        cache_times = {}

        @functools.wraps(func)
        def wrapper(value: Any, **kwargs) -> Any:
            # Create cache key from function name, value, and kwargs
            cache_key = hashlib.md5(
                json.dumps(
                    {"func": func.__name__, "value": str(value), "kwargs": kwargs},
                    sort_keys=True,
                ).encode()
            ).hexdigest()

            # Check cache
            current_time = time.time()
            if cache_key in cache:
                cached_time = cache_times.get(cache_key, 0)
                if current_time - cached_time < ttl:
                    log.debug(f"Cache hit for {func.__name__}")
                    return cache[cache_key]

            # Call function and cache result
            result = func(value, **kwargs)
            cache[cache_key] = result
            cache_times[cache_key] = current_time

            # Clean old entries if cache is too large
            if len(cache) > 1000:
                expired_keys = [
                    k for k, t in cache_times.items() if current_time - t > ttl
                ]
                for k in expired_keys:
                    cache.pop(k, None)
                    cache_times.pop(k, None)

            return result

        return wrapper

    return decorator


def validate_input(schema: Dict[str, type]):
    """Decorator to validate input against a schema."""

    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(value: Any, **kwargs) -> Any:
            # For dict inputs, validate keys and types
            if isinstance(value, dict):
                for key, expected_type in schema.items():
                    if key in value and not isinstance(value[key], expected_type):
                        return {
                            "value": None,
                            "error": f"Invalid type for {key}: expected {expected_type.__name__}",
                            "meta": {"op": func.__name__},
                        }

            return func(value, **kwargs)

        return wrapper

    return decorator
