"""Structured logging setup using structlog."""

from __future__ import annotations

import logging
import sys
from typing import TYPE_CHECKING, Any

import structlog

if TYPE_CHECKING:
    from pathlib import Path

    from structlog.types import Processor

    from curadise_agent.config.schema import LoggingConfig


def _add_log_level(
    logger: logging.Logger, method_name: str, event_dict: dict[str, Any]
) -> dict[str, Any]:
    """Add log level to event dict."""
    event_dict["level"] = method_name.upper()
    return event_dict


def _add_agent_context(logger: Any, method_name: str, event_dict: dict[str, Any]) -> dict[str, Any]:
    """Add agent context to all log messages."""
    event_dict.setdefault("agent", "curadise-agent")
    return event_dict


def setup_logging(config: LoggingConfig, agent_id: str | None = None) -> structlog.BoundLogger:
    """
    Configure structured logging for the agent.

    Args:
        config: Logging configuration
        agent_id: Optional agent ID to include in all logs

    Returns:
        Configured structlog logger
    """
    # Determine log level
    log_level = getattr(logging, config.level.value, logging.INFO)

    # Build processor chain
    shared_processors: list[Processor] = [
        structlog.contextvars.merge_contextvars,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.UnicodeDecoder(),
        _add_agent_context,  # type: ignore[list-item]
    ]

    if config.format == "json":
        # JSON output for production
        renderer: Processor = structlog.processors.JSONRenderer()
    else:
        # Console output for development
        renderer = structlog.dev.ConsoleRenderer(colors=True)

    # Configure structlog
    structlog.configure(
        processors=[
            *shared_processors,
            structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
        ],
        logger_factory=structlog.stdlib.LoggerFactory(),
        wrapper_class=structlog.stdlib.BoundLogger,
        cache_logger_on_first_use=True,
    )

    # Configure standard library logging
    formatter = structlog.stdlib.ProcessorFormatter(
        foreign_pre_chain=shared_processors,
        processors=[
            structlog.stdlib.ProcessorFormatter.remove_processors_meta,
            renderer,
        ],
    )

    # Setup handlers
    handlers: list[logging.Handler] = []

    # Console handler
    console_handler = logging.StreamHandler(sys.stderr)
    console_handler.setFormatter(formatter)
    handlers.append(console_handler)

    # File handler if configured
    if config.file is not None:
        _ensure_log_directory(config.file)
        from logging.handlers import RotatingFileHandler

        file_handler = RotatingFileHandler(
            config.file,
            maxBytes=config.max_size_mb * 1024 * 1024,
            backupCount=config.backup_count,
            encoding="utf-8",
        )
        file_handler.setFormatter(formatter)
        handlers.append(file_handler)

    # Configure root logger
    root_logger = logging.getLogger()
    root_logger.setLevel(log_level)
    root_logger.handlers = handlers

    # Silence noisy libraries
    logging.getLogger("httpx").setLevel(logging.WARNING)
    logging.getLogger("httpcore").setLevel(logging.WARNING)
    logging.getLogger("watchfiles").setLevel(logging.WARNING)

    # Get the logger instance
    log: structlog.BoundLogger = structlog.get_logger("curadise_agent")

    # Bind agent_id if available
    if agent_id:
        log = log.bind(agent_id=agent_id)

    return log


def _ensure_log_directory(log_path: Path) -> None:
    """Ensure the log directory exists."""
    log_path.parent.mkdir(parents=True, exist_ok=True)


def get_logger(name: str | None = None) -> structlog.BoundLogger:
    """Get a logger instance."""
    if name:
        return structlog.get_logger(name)  # type: ignore[no-any-return]
    return structlog.get_logger("curadise_agent")  # type: ignore[no-any-return]
