"""
Comprehensive Logging System for CNPERP ERP

This module provides enhanced logging capabilities with:
- Automatic error context capture (request info, user info, etc.)
- Performance tracking with detailed metrics
- Structured logging for better searchability
- Automatic exception logging with full stack traces
- Request/Response logging for API debugging
"""

import logging
import functools
import time
import traceback
import sys
from typing import Any, Callable, Optional, Dict
from datetime import datetime
from pathlib import Path
import json

from .logger import get_logger

# Create specialized loggers
request_logger = get_logger("requests")
error_logger = get_logger("errors")
performance_logger = get_logger("performance")
db_logger = get_logger("database")
api_logger = get_logger("api")


class RequestContext:
    """Store request context for logging"""
    _context = {}

    @classmethod
    def set(cls, key: str, value: Any):
        cls._context[key] = value

    @classmethod
    def get(cls, key: str, default=None):
        return cls._context.get(key, default)

    @classmethod
    def clear(cls):
        cls._context = {}

    @classmethod
    def get_all(cls) -> Dict:
        return cls._context.copy()


def log_error_with_context(
    logger: logging.Logger,
    error: Exception,
    message: str = "",
    **additional_context
):
    """
    Log an error with comprehensive context information.

    Args:
        logger: Logger instance to use
        error: The exception that occurred
        message: Custom error message
        **additional_context: Additional context key-value pairs
    """
    context = RequestContext.get_all()
    context.update(additional_context)

    error_details = {
        "error_type": type(error).__name__,
        "error_message": str(error),
        "custom_message": message,
        "timestamp": datetime.now().isoformat(),
        "traceback": traceback.format_exc(),
        **context
    }

    log_message = f"{message}\n" if message else ""
    log_message += f"Error Type: {error_details['error_type']}\n"
    log_message += f"Error Message: {error_details['error_message']}\n"

    if context:
        log_message += "Context:\n"
        for key, value in context.items():
            log_message += f"  {key}: {value}\n"

    log_message += f"\nFull Traceback:\n{error_details['traceback']}"

    logger.error(log_message)


def comprehensive_error_handler(
    logger: Optional[logging.Logger] = None,
    reraise: bool = True,
    log_args: bool = True,
    custom_message: str = ""
):
    """
    Decorator that provides comprehensive error logging.

    Args:
        logger: Logger to use (defaults to function module logger)
        reraise: Whether to re-raise the exception after logging
        log_args: Whether to log function arguments
        custom_message: Custom message prefix for the error
    """
    def decorator(func: Callable) -> Callable:
        nonlocal logger
        if logger is None:
            logger = get_logger(func.__module__)

        @functools.wraps(func)
        def sync_wrapper(*args, **kwargs) -> Any:
            func_name = f"{func.__module__}.{func.__name__}"

            # Log function arguments if requested
            if log_args:
                args_str = ", ".join([repr(arg) for arg in args[:3]])  # Limit to first 3 args
                kwargs_str = ", ".join([f"{k}={repr(v)}" for k, v in list(kwargs.items())[:3]])
                logger.debug(f"Calling {func_name}({args_str}, {kwargs_str})")

            try:
                result = func(*args, **kwargs)
                return result
            except Exception as e:
                error_context = {
                    "function": func_name,
                    "args_count": len(args),
                    "kwargs_keys": list(kwargs.keys())
                }

                message = custom_message or f"Error in {func_name}"
                log_error_with_context(logger, e, message, **error_context)

                if reraise:
                    raise
                return None

        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs) -> Any:
            func_name = f"{func.__module__}.{func.__name__}"

            if log_args:
                args_str = ", ".join([repr(arg) for arg in args[:3]])
                kwargs_str = ", ".join([f"{k}={repr(v)}" for k, v in list(kwargs.items())[:3]])
                logger.debug(f"Calling async {func_name}({args_str}, {kwargs_str})")

            try:
                result = await func(*args, **kwargs)
                return result
            except Exception as e:
                error_context = {
                    "function": func_name,
                    "args_count": len(args),
                    "kwargs_keys": list(kwargs.keys()),
                    "async": True
                }

                message = custom_message or f"Error in async {func_name}"
                log_error_with_context(logger, e, message, **error_context)

                if reraise:
                    raise
                return None

        # Return appropriate wrapper based on function type
        if asyncio.iscoroutinefunction(func):
            return async_wrapper
        return sync_wrapper

    return decorator


def log_performance(
    logger: Optional[logging.Logger] = None,
    threshold_seconds: float = 1.0,
    log_slow_only: bool = False
):
    """
    Decorator that logs function performance metrics.

    Args:
        logger: Logger to use (defaults to performance logger)
        threshold_seconds: Log warning if execution exceeds this time
        log_slow_only: Only log if execution time exceeds threshold
    """
    def decorator(func: Callable) -> Callable:
        nonlocal logger
        if logger is None:
            logger = performance_logger

        @functools.wraps(func)
        def sync_wrapper(*args, **kwargs) -> Any:
            func_name = f"{func.__module__}.{func.__name__}"
            start_time = time.time()

            try:
                result = func(*args, **kwargs)
                execution_time = time.time() - start_time

                if log_slow_only and execution_time < threshold_seconds:
                    return result

                log_level = logging.WARNING if execution_time > threshold_seconds else logging.INFO
                logger.log(
                    log_level,
                    f"{func_name} completed in {execution_time:.3f}s"
                    + (f" (SLOW - threshold: {threshold_seconds}s)" if execution_time > threshold_seconds else "")
                )

                return result
            except Exception as e:
                execution_time = time.time() - start_time
                logger.error(f"{func_name} failed after {execution_time:.3f}s: {str(e)}")
                raise

        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs) -> Any:
            func_name = f"{func.__module__}.{func.__name__}"
            start_time = time.time()

            try:
                result = await func(*args, **kwargs)
                execution_time = time.time() - start_time

                if log_slow_only and execution_time < threshold_seconds:
                    return result

                log_level = logging.WARNING if execution_time > threshold_seconds else logging.INFO
                logger.log(
                    log_level,
                    f"async {func_name} completed in {execution_time:.3f}s"
                    + (f" (SLOW - threshold: {threshold_seconds}s)" if execution_time > threshold_seconds else "")
                )

                return result
            except Exception as e:
                execution_time = time.time() - start_time
                logger.error(f"async {func_name} failed after {execution_time:.3f}s: {str(e)}")
                raise

        # Return appropriate wrapper
        if asyncio.iscoroutinefunction(func):
            return async_wrapper
        return sync_wrapper

    return decorator


def log_api_request(
    logger: Optional[logging.Logger] = None,
    log_request_body: bool = True,
    log_response_body: bool = False
):
    """
    Decorator for API endpoint logging.

    Args:
        logger: Logger to use (defaults to api logger)
        log_request_body: Whether to log request body
        log_response_body: Whether to log response body
    """
    def decorator(func: Callable) -> Callable:
        nonlocal logger
        if logger is None:
            logger = api_logger

        @functools.wraps(func)
        async def wrapper(*args, **kwargs) -> Any:
            func_name = f"{func.__module__}.{func.__name__}"
            start_time = time.time()

            # Try to extract request info from kwargs
            request_info = {}
            for arg in args:
                if hasattr(arg, 'method') and hasattr(arg, 'url'):
                    request_info['method'] = arg.method
                    request_info['url'] = str(arg.url)
                    request_info['client'] = getattr(arg.client, 'host', 'unknown') if hasattr(arg, 'client') else 'unknown'
                    break

            logger.info(f"API Request: {request_info.get('method', 'N/A')} {request_info.get('url', func_name)}")

            try:
                result = await func(*args, **kwargs)
                execution_time = time.time() - start_time

                status_code = getattr(result, 'status_code', 'N/A')
                logger.info(
                    f"API Response: {request_info.get('method', 'N/A')} {request_info.get('url', func_name)} "
                    f"- Status: {status_code} - Time: {execution_time:.3f}s"
                )

                return result
            except Exception as e:
                execution_time = time.time() - start_time
                logger.error(
                    f"API Error: {request_info.get('method', 'N/A')} {request_info.get('url', func_name)} "
                    f"- Error: {type(e).__name__}: {str(e)} - Time: {execution_time:.3f}s"
                )
                raise

        return wrapper

    return decorator


def log_database_operation(
    logger: Optional[logging.Logger] = None,
    operation_type: str = "query"
):
    """
    Decorator for database operation logging.

    Args:
        logger: Logger to use (defaults to db logger)
        operation_type: Type of operation (query, insert, update, delete)
    """
    def decorator(func: Callable) -> Callable:
        nonlocal logger
        if logger is None:
            logger = db_logger

        @functools.wraps(func)
        def wrapper(*args, **kwargs) -> Any:
            func_name = f"{func.__module__}.{func.__name__}"
            start_time = time.time()

            logger.debug(f"DB {operation_type}: {func_name}")

            try:
                result = func(*args, **kwargs)
                execution_time = time.time() - start_time

                if execution_time > 1.0:
                    logger.warning(f"SLOW DB {operation_type}: {func_name} took {execution_time:.3f}s")
                else:
                    logger.debug(f"DB {operation_type} completed: {func_name} in {execution_time:.3f}s")

                return result
            except Exception as e:
                execution_time = time.time() - start_time
                logger.error(
                    f"DB {operation_type} failed: {func_name} after {execution_time:.3f}s\n"
                    f"Error: {type(e).__name__}: {str(e)}\n"
                    f"Traceback: {traceback.format_exc()}"
                )
                raise

        return wrapper

    return decorator


# Import asyncio at the end to avoid circular imports
import asyncio
