"""Logger Configuration Module.

Provides centralized logging configuration for the Sec Interp plugin.
"""

from __future__ import annotations

import logging
import os
import sys
from logging.handlers import RotatingFileHandler
from pathlib import Path
from typing import Any

from qgis.core import Qgis, QgsMessageLog


class ImmediateFlushFileHandler(RotatingFileHandler):
    """File handler that flushes immediately after each write.

    This ensures logs are written to disk before a crash occurs.
    Uses os.fsync() to force OS-level write to disk.
    """

    def emit(self, record: logging.LogRecord) -> None:
        """Emit a log record and flush immediately.

        Args:
            record: The log record to emit.

        """
        super().emit(record)
        self.flush()
        # Force OS-level write to disk (slower but safer for crash analysis)
        try:
            if hasattr(self.stream, "fileno"):
                os.fsync(self.stream.fileno())
        except (OSError, AttributeError):
            # If fsync fails, continue anyway
            pass


class QgsLogHandler(logging.Handler):
    """Custom logging handler that writes to QGIS message log."""

    def __init__(self, tag: str = "SecInterp") -> None:
        """Initialize handler with a specific tag for QGIS message log."""
        super().__init__()
        self.tag = tag

    def emit(self, record: logging.LogRecord) -> None:
        """Emit a log record to QGIS message log safely."""
        try:
            from qgis.PyQt.QtCore import QCoreApplication, QThread

            msg = self.format(record)

            # Map Python logging levels to QGIS levels
            if record.levelno >= logging.ERROR:
                level = Qgis.Critical
            elif record.levelno >= logging.WARNING:
                level = Qgis.Warning
            elif record.levelno >= logging.INFO:
                level = Qgis.Info
            else:
                level = Qgis.Info

            # Critical: UI updates from background threads cause segfaults in QGIS
            instance = QCoreApplication.instance()
            if instance and QThread.currentThread() == instance.thread():
                QgsMessageLog.logMessage(msg, self.tag, level)
            else:
                # Fallback to standard error for background threads to avoid potential UI hangs
                # sys.stderr is safer and doesn't trigger "print" deviations
                sys.stderr.write(f"[{self.tag}] (BG) {msg}\n")
        except Exception:
            self.handleError(record)


ROOT_LOGGER_NAME = "SecInterp"


def setup_logging(level: int = logging.DEBUG) -> logging.Logger:
    """Configure the root logger for the plugin.

    This should be called once at plugin initialization. It sets up
    handlers for QGIS message log, file logging with rotation, and stderr.

    Args:
        level: Logging level for the root logger. Defaults to DEBUG.

    Returns:
        logging.Logger: The configured root logger.

    """
    root_logger = logging.getLogger(ROOT_LOGGER_NAME)

    # Only configure if not already configured
    if not root_logger.handlers:
        root_logger.setLevel(level)

        # 1. Create QGIS message log handler (for UI)
        qgis_handler = QgsLogHandler(tag=ROOT_LOGGER_NAME)
        qgis_handler.setLevel(logging.INFO)
        qgis_formatter = logging.Formatter("%(levelname)s - %(message)s")
        qgis_handler.setFormatter(qgis_formatter)
        root_logger.addHandler(qgis_handler)

        # 2. Create file handler for detailed crash analysis
        try:
            # Determine log directory (in repository root)
            # Find the root of the project (where logger_config.py is)
            root_dir = Path(__file__).parent
            log_dir = root_dir / "logs"
            log_dir.mkdir(exist_ok=True)

            log_file = log_dir / "sec_interp_debug.log"

            # Use ImmediateFlushFileHandler for crash analysis
            file_handler = ImmediateFlushFileHandler(
                log_file,
                maxBytes=10 * 1024 * 1024,  # 10MB
                backupCount=5,
                encoding="utf-8",
            )
            file_handler.setLevel(logging.DEBUG)

            # Detailed formatter for file logs
            file_formatter = logging.Formatter(
                "%(asctime)s | %(levelname)-8s | %(name)s | "
                "%(funcName)s:%(lineno)d | Thread-%(thread)d | %(message)s",
                datefmt="%Y-%m-%d %H:%M:%S",
            )
            file_handler.setFormatter(file_formatter)
            root_logger.addHandler(file_handler)

            # 3. Add stderr handler as backup for crash scenarios
            stderr_handler = logging.StreamHandler(sys.stderr)
            stderr_handler.setLevel(
                logging.WARNING
            )  # Only warnings and errors to stderr
            stderr_handler.setFormatter(file_formatter)
            root_logger.addHandler(stderr_handler)

            # Log the initialization
            root_logger.debug(f"Logging system initialized. Root Level: {level}")
            root_logger.debug(f"File logging path: {log_file}")

        except Exception as e:
            # If file logging fails, continue with QGIS logging only
            QgsMessageLog.logMessage(
                f"Warning: Could not initialize file logging: {e}",
                ROOT_LOGGER_NAME,
                Qgis.Warning,
            )

        # Ensure propagation is enabled for children
        root_logger.propagate = True

    return root_logger


def get_logger(name: str | None = None) -> logging.Logger:
    """Get a configured logger for a plugin module.

    Args:
        name: Name of the logger (typically __name__ from calling module).
            If None, returns the root plugin logger.

    Returns:
        logging.Logger: Configured logger instance.

    """
    if name is None or name == ROOT_LOGGER_NAME:
        return logging.getLogger(ROOT_LOGGER_NAME)

    # Ensure we use the hierarchy (SecInterp.something.else)
    full_name = (
        name
        if name.startswith(ROOT_LOGGER_NAME + ".")
        else f"{ROOT_LOGGER_NAME}.{name}"
    )

    logger = logging.getLogger(full_name)

    # Auto-initialize if root has no handlers (for tests or standalone usage)
    root = logging.getLogger(ROOT_LOGGER_NAME)
    if not root.handlers and name != ROOT_LOGGER_NAME:
        setup_logging()

    return logger


def log_critical_operation(
    logger: logging.Logger, operation_name: str, **context: Any
) -> None:
    """Log a critical operation with maximum persistence.

    Use this before operations that might crash QGIS (e.g., canvas operations,
    rubber band manipulation, tool activation).

    Args:
        logger: Logger instance.
        operation_name: Name of the operation.
        **context: Additional context to log.

    """
    import datetime

    msg = f"CRITICAL_OP: {operation_name}"
    if context:
        ctx_str = ", ".join(f"{k}={v}" for k, v in context.items())
        msg += f" | {ctx_str}"

    # Log through normal channels
    logger.debug(msg)

    # Also write directly to stderr with timestamp (bypasses all buffering)
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
    sys.stderr.write(f"[{timestamp}] {msg}\n")
    sys.stderr.flush()

    # Try to force OS sync on stderr too
    try:
        if hasattr(sys.stderr, "fileno"):
            os.fsync(sys.stderr.fileno())
    except (OSError, AttributeError):
        pass
