# Copyright [2025] [IBM]
# Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
# See the LICENSE file in the project root for license information.

"""
Logging utilities and setup functions for the Data Intelligence MCP Server.

This module provides utilities for setting up and configuring logging
with custom formats, traceability, and structured output.
"""

import logging
# Removed unused import: os
import sys
from pathlib import Path
from typing import Optional

from .constants import (
    LOGGING_LEVELS,
    JSON_LOGGING,
    HUMAN_LOGGING,
    LOGGING_LEVEL,
    LOGGING_FORMAT,
    CONTAINER_NAME,
    ENVIRONMENT,
    BUILD_VERSION,
    LOG_FILE_PATH,
    set_parameter_value,
    parameter_values
)
from .filter import LoggingTraceabilityFilter

def setup_logging(
    logger_name: Optional[str] = None,
    log_level: Optional[str] = None,
    log_format: Optional[str] = None,
    log_file_path: Optional[str] = None
) -> logging.Logger:
    """
    Configure logging based on specified parameters.

    This function sets up a logger with either human-readable or JSON format,
    includes traceability information, and configures appropriate handlers.

    Args:
        logger_name: Name for the logger (defaults to container name from config)
        log_level: Logging level (defaults to config value)
        log_format: Logging format type (defaults to config value)
        log_file_path: File to which logs are written. For stdio mode, this is mandatory
                       as stdio is used by the MCP protocol itself

    Returns:
        logging.Logger: Configured logger instance

    Raises:
        ValueError: If invalid logging level is specified
    """
    # Set parameter values from environment or defaults
    retrieve_parameters_from_env()

    # Use provided values or fall back to configuration
    final_logger_name = logger_name or parameter_values[CONTAINER_NAME]
    final_log_level = log_level or parameter_values[LOGGING_LEVEL]
    final_log_format = log_format or parameter_values[LOGGING_FORMAT]
    final_log_file_path = log_file_path or parameter_values.get(LOG_FILE_PATH)

    # Get logger instance
    logger = logging.getLogger(final_logger_name)

    # We don't want to propagate the logger to the root logger
    logger.propagate = False

    # Return existing logger if already configured
    if logger.handlers:
        return logger

    # Validate logging level
    if final_log_level.upper() not in LOGGING_LEVELS:
        raise ValueError(
            f"Invalid logging level '{final_log_level.upper()}'. "
            f"Supported levels: {list(LOGGING_LEVELS.keys())}"
        )

    # Set logger level
    logger.setLevel(LOGGING_LEVELS[final_log_level.upper()])

    # Create handler based on configuration
    if final_log_file_path:
        # File logging
        log_path = Path(final_log_file_path)
        log_path.parent.mkdir(parents=True, exist_ok=True)
        ch = logging.FileHandler(log_path)
    else:
        # Console logging - CRITICAL: Use stderr for MCP stdio transport
        # In stdio mode, STDOUT is exclusively used by FastMCP for JSON-RPC protocol
        # Writing logs to stdout corrupts the protocol stream causing client-side
        # JSON parse errors like "Expected ',' or '}' at position X"
        ch = logging.StreamHandler(sys.stderr)

    # Configure formatter based on format type
    if final_log_format == HUMAN_LOGGING:
        formatter = logging.Formatter(
            "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        )
    elif final_log_format == JSON_LOGGING:
        formatter = logging.Formatter(
            '{"environment": "'
            + parameter_values[ENVIRONMENT]
            + '", "build_version": "'
            + parameter_values[BUILD_VERSION]
            + '", "timestamp": "%(asctime)s", "appname": "%(name)s"'
            + ', "loglevel": "%(levelname)s", "message": "%(message)s"'
            + ', "threadId": "%(thread)d", "thread": "%(threadName)s"'
            + ', "sequence_number": "%(sequence_number)d"'
            + ', "transaction_id": "%(transaction_id)s"'
            + ', "trace_id": "%(trace_id)s"'
            + '", "line": "%(lineno)d", "method_name": "%(funcName)s"'
            + ', "class_name": "%(module)s"'
            + '"}'
        )
        # Add traceability filter for JSON logging
        ch.addFilter(LoggingTraceabilityFilter())
    else:
        # Custom format provided
        formatter = logging.Formatter(final_log_format)

    # Set formatter and add handler
    ch.setFormatter(formatter)
    logger.addHandler(ch)

    if final_log_file_path:
        # Add console handler for ERROR and CRITICAL levels
        console_handler = logging.StreamHandler(sys.stderr)
        console_handler.setLevel(logging.ERROR)
        console_formatter = logging.Formatter(
            "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        )
        console_handler.setFormatter(console_formatter)
        logger.addHandler(console_handler)

    return logger


def get_logger(name: Optional[str] = None) -> logging.Logger:
    """
    Get a logger instance with the specified name.

    Args:
        name: Logger name (defaults to container name from config)

    Returns:
        logging.Logger: Configured logger instance
    """
    return setup_logging(logger_name=name)


def retrieve_parameters_from_env() -> None:
    """
    Get and verify configuration from the environment.

    This function reads all configuration parameters from environment variables
    and stores them in the parameter_values dictionary.
    """
    from .constants import DEFAULT_PARAMETERS

    for parameter_name in DEFAULT_PARAMETERS.keys():
        set_parameter_value(parameter_name)


# Global logger instance
LOGGER = setup_logging()
