"""
Universal Python logger with Rich console output and file persistence.

Auto-detects project root by looking for Python project markers:
- pyproject.toml
- setup.py
- requirements.txt
- .git

Usage:
    from sdkrouter_tools.logging import get_logger

    log = get_logger(__name__)
    log.info("Hello world")
    log.error("Something failed", exc_info=True)

    # Or with custom settings
    log = get_logger(__name__, level="DEBUG", log_to_file=True)
"""

from __future__ import annotations

import logging
import sys
from datetime import datetime
from pathlib import Path
from typing import Any, Literal

from rich.console import Console
from rich.logging import RichHandler
from rich.traceback import install as install_rich_traceback

__all__ = [
    "get_logger",
    "setup_logging",
    "find_project_root",
    "get_log_dir",
    "reset_logging",
    "debug",
    "info",
    "warning",
    "error",
    "critical",
    "LogLevel",
]

# Project markers to search for (in priority order)
PROJECT_MARKERS = [
    "pyproject.toml",
    "setup.py",
    "setup.cfg",
    "requirements.txt",
    "Pipfile",
    ".git",
]

LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]

# Cache for project root
_project_root_cache: Path | None = None
_logging_configured: bool = False


def find_project_root(start_path: Path | None = None) -> Path | None:
    """
    Find Python project root by searching for project markers.

    Searches upward from start_path (default: cwd) looking for:
    - pyproject.toml
    - setup.py
    - requirements.txt
    - .git

    Returns:
        Path to project root, or None if not found
    """
    global _project_root_cache

    if _project_root_cache is not None:
        return _project_root_cache

    if start_path is None:
        start_path = Path.cwd()

    path = start_path.resolve()

    for current in [path, *list(path.parents)]:
        for marker in PROJECT_MARKERS:
            if (current / marker).exists():
                _project_root_cache = current
                return current

    return None


def get_log_dir(app_name: str = "sdkrouter_tools") -> Path:
    """
    Get log directory path.

    Priority:
    1. Project root / logs (if project root found)
    2. ~/Library/Logs/{app_name} (macOS)
    3. ~/AppData/Local/{app_name}/logs (Windows)
    4. ~/.local/logs/{app_name} (Linux)

    Creates directory if it doesn't exist.

    Args:
        app_name: Application name for fallback directory

    Returns:
        Path to log directory
    """
    # Try project root first
    project_root = find_project_root()
    if project_root:
        log_dir = project_root / "logs"
    else:
        # Fallback to home directory
        home = Path.home()
        if sys.platform == "darwin":
            log_dir = home / "Library" / "Logs" / app_name
        elif sys.platform == "win32":
            log_dir = home / "AppData" / "Local" / app_name / "logs"
        else:  # Linux and others
            log_dir = home / ".local" / "logs" / app_name

    # Create directory if needed
    log_dir.mkdir(parents=True, exist_ok=True)
    return log_dir


def setup_logging(
    level: LogLevel = "INFO",
    log_to_file: bool = True,
    log_to_console: bool = True,
    app_name: str = "sdkrouter_tools",
    rich_tracebacks: bool = True,
) -> None:
    """
    Configure root logger with Rich console and file handlers.

    Args:
        level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
        log_to_file: Whether to write logs to file
        log_to_console: Whether to output to console
        app_name: Application name for log file naming
        rich_tracebacks: Install Rich traceback handler for exceptions
    """
    global _logging_configured

    if _logging_configured:
        return

    root_logger = logging.getLogger()
    root_logger.setLevel(getattr(logging, level))

    # Clear existing handlers
    root_logger.handlers.clear()

    # Console handler with Rich
    if log_to_console:
        console = Console(stderr=True)
        console_handler: logging.Handler = RichHandler(
            console=console,
            show_time=True,
            show_path=True,
            rich_tracebacks=True,
            tracebacks_show_locals=True,
            markup=True,
        )
        console_handler.setFormatter(logging.Formatter("%(message)s"))
        root_logger.addHandler(console_handler)

    # File handler
    if log_to_file:
        log_dir = get_log_dir(app_name)
        date_str = datetime.now().strftime("%Y-%m-%d")
        log_file = log_dir / f"{app_name}_{date_str}.log"

        file_handler = logging.FileHandler(log_file, encoding="utf-8")
        file_handler.setFormatter(
            logging.Formatter(
                "%(asctime)s | %(levelname)-8s | %(name)s:%(lineno)d - %(message)s",
                datefmt="%Y-%m-%d %H:%M:%S",
            )
        )
        root_logger.addHandler(file_handler)

    # Install Rich tracebacks globally
    if rich_tracebacks:
        install_rich_traceback(show_locals=True, suppress=[])

    _logging_configured = True


def get_logger(
    name: str | None = None,
    level: LogLevel | None = None,
    log_to_file: bool = True,
    app_name: str = "sdkrouter_tools",
) -> logging.Logger:
    """
    Get a configured logger instance.

    On first call, sets up the logging system with Rich console output
    and file persistence.

    Args:
        name: Logger name (typically __name__)
        level: Override log level (default: INFO)
        log_to_file: Whether to write to log file
        app_name: Application name for log file

    Returns:
        Configured logger instance

    Example:
        log = get_logger(__name__)
        log.info("Starting process")
        log.debug("Debug details: %s", data)
        log.error("Failed!", exc_info=True)
    """
    # Setup logging system on first call
    setup_logging(
        level=level or "INFO",
        log_to_file=log_to_file,
        app_name=app_name,
    )

    logger = logging.getLogger(name)

    # Override level if specified
    if level:
        logger.setLevel(getattr(logging, level))

    return logger


def reset_logging() -> None:
    """
    Reset logging configuration. Useful for testing.
    """
    global _logging_configured, _project_root_cache
    _logging_configured = False
    _project_root_cache = None

    # Clear root logger handlers
    root_logger = logging.getLogger()
    root_logger.handlers.clear()


# Convenience aliases
def debug(msg: str, *args: Any, **kwargs: Any) -> None:
    """Log a debug message."""
    get_logger().debug(msg, *args, **kwargs)


def info(msg: str, *args: Any, **kwargs: Any) -> None:
    """Log an info message."""
    get_logger().info(msg, *args, **kwargs)


def warning(msg: str, *args: Any, **kwargs: Any) -> None:
    """Log a warning message."""
    get_logger().warning(msg, *args, **kwargs)


def error(msg: str, *args: Any, **kwargs: Any) -> None:
    """Log an error message."""
    get_logger().error(msg, *args, **kwargs)


def critical(msg: str, *args: Any, **kwargs: Any) -> None:
    """Log a critical message."""
    get_logger().critical(msg, *args, **kwargs)
