"""
Logs Screen module
"""

import logging
import threading
from typing import cast

from rich.markup import escape
from textual.app import ComposeResult
from textual.binding import Binding
from textual.screen import Screen
from textual.widgets import Footer, Header, RichLog

logger = logging.getLogger("exosphere.ui.logs")


class RichLogFormatter(logging.Formatter):
    """Custom formatter that adds Rich markup with level-specific colors."""

    LEVEL_COLORS = {
        "DEBUG": "dim",
        "INFO": "green",
        "WARNING": "yellow",
        "ERROR": "red",
        "CRITICAL": "bold red",
    }

    def format(self, record: logging.LogRecord) -> str:
        # Get the color for this log level
        level_color = self.LEVEL_COLORS.get(record.levelname, "white")

        # Create our custom format with proper alignment and colors
        timestamp = self.formatTime(record, self.datefmt)
        logger_name = record.name
        level_name = record.levelname

        # Escape rich markup in the log message
        # Log strings should never contained unescaped markup.
        message = escape(record.getMessage())

        # Strip "exosphere." prefix to save space
        if logger_name.startswith("exosphere."):
            logger_name = logger_name[10:]  # Remove "exosphere."

        # Truncate logger name if still too long, keeping the most relevant part
        if len(logger_name) > 15:
            # Keep the last 15 characters (usually the most specific part)
            logger_name = "..." + logger_name[-12:]

        # Format with Rich markup and proper alignment
        formatted = (
            f"{timestamp} "
            f"[{level_color}]{level_name:<8}[/{level_color}] "
            f"[cyan]{logger_name:<15}[/cyan] "
            f"{message}"
        )

        return formatted


class UILogHandler(logging.Handler):
    """
    Custom logging handler to display logs in the UI

    Involves a running buffer to store logs until the log widget is set,
    which generally happens when the Logs screen is mounted.

    The log handler will backfill the log widget with any buffered logs
    at that point, and any new logs will be written directly to the
    widget, in the log screen.

    The buffering should be reasonably thread-safe, but it is a very
    clumsy and naive attempt at reentrant locking, since the
    application is made of a mix of threads and async coroutines.
    """

    # Class level buffer shared across instances, holds logs until
    # the widget is available and ready to receive them.
    _buffer: list[str] = []
    _buffer_lock = threading.RLock()

    @classmethod
    def get_buffer_contents(cls) -> list[str]:
        """
        Get a copy of the current buffer contents.
        Thread-safe.
        """
        with cls._buffer_lock:
            return cls._buffer.copy()

    @classmethod
    def clear_buffer(cls) -> None:
        """
        Clear the local buffer.
        Thread-safe.
        """
        with cls._buffer_lock:
            cls._buffer.clear()

    @classmethod
    def get_buffer_size(cls) -> int:
        """Get the current buffer size."""
        with cls._buffer_lock:
            return len(cls._buffer)

    def emit(self, record: logging.LogRecord) -> None:
        msg = self.format(record)
        if hasattr(self, "log_widget") and self.log_widget:
            self.log_widget.write(msg)
            return

        # If log_widget is not set, store message in buffer
        with self._buffer_lock:
            self._buffer.append(msg)

    def set_log_widget(self, log_widget: RichLog | None) -> None:
        """Set the log widget to write logs to."""
        self.log_widget = log_widget

        # Widget has been cleared, no need to do anything else
        if not self.log_widget:
            return

        logging.getLogger("exosphere.ui").debug(
            "Flushing buffered logs to the log widget."
        )

        # Flush any buffered logs to the widget
        with self._buffer_lock:
            for msg in self._buffer:
                try:
                    self.log_widget.write(msg)
                except Exception as e:
                    logging.getLogger("exosphere.ui").error(
                        f"Error writing buffered log message to log pane!: {str(e)}"
                    )

            self._buffer.clear()


class LogsScreen(Screen):
    """Screen for the logs."""

    CSS_PATH = "style.tcss"

    BINDINGS = [
        Binding("l", "app.none", show=False),
    ]

    def compose(self) -> ComposeResult:
        """Compose the logs layout."""

        # Create RichLog widget for displaying logs
        self.log_widget = RichLog(
            name="logs", auto_scroll=True, markup=True, highlight=True
        )

        yield Header()
        yield self.log_widget
        yield Footer()

    def on_mount(self) -> None:
        """Set the title and subtitle of the logs."""
        self.title = "Exosphere"
        self.sub_title = "Logs Viewer"

        # Initialize the UILogHandler and set it to the app's log widget
        from exosphere.ui.app import ExosphereUi

        app = cast(ExosphereUi, self.app)

        if app.ui_log_handler is None:
            logger.error("UI Log handler is not initialized. Cannot set log widget!")
            return

        app.ui_log_handler.set_log_widget(self.log_widget)

        logger.debug("Log view initialized")

    def on_unmount(self) -> None:
        """Clean up the log widget when the screen is unmounted."""
        from exosphere.ui.app import ExosphereUi

        app = cast(ExosphereUi, self.app)

        if app.ui_log_handler is None:
            logger.debug("UI Log handler is not initialized, nothing to clean up")
            return

        app.ui_log_handler.set_log_widget(None)
