"""Message coordination system for preventing race conditions.

This coordinator solves the fundamental race condition where multiple
message writing systems interfere with each other, causing messages
to be overwritten or cleared unexpectedly.

IMPORTANT: All terminal state changes (input rendering, clearing, buffer
transitions) should go through this coordinator to prevent state bugs.

This module provides atomic message display coordination and unified
state management to prevent interference between different message
writing systems.
"""

import logging
from typing import List, Tuple, Dict, Any, Optional

# Import modern renderer and display filter system
from .message_renderer import ModernMessageRenderer, DisplayFilterRegistry, MessageType

logger = logging.getLogger(__name__)


class MessageDisplayCoordinator:
    """Coordinates message display AND render state to prevent interference.

    Key Features:
    - Atomic message sequences (all messages display together)
    - Unified state management (prevents clearing conflicts)
    - Proper ordering (system messages before responses)
    - Protection from interference (no race conditions)
    - Buffer transition management (modal open/close state preservation)
    """

    def __init__(self, terminal_renderer):
        """Initialize message display coordinator.

        Args:
            terminal_renderer: TerminalRenderer instance for display
        """
        self.terminal_renderer = terminal_renderer
        self.message_queue: List[Tuple[str, str, Dict[str, Any]]] = []
        self.is_displaying = False

        # Navigation awareness - prevents render conflicts with status navigation
        self.navigation_active: bool = False

        # Saved state for buffer transitions (modal, fullscreen, etc.)
        self._saved_main_buffer_state: Optional[Dict[str, Any]] = None
        self._in_alternate_buffer = False

        logger.debug("MessageDisplayCoordinator initialized")

    def _capture_render_state(self) -> Dict[str, Any]:
        """Capture current render state for later restoration.

        Returns:
            Dictionary containing render state snapshot.
        """
        return {
            "writing_messages": self.terminal_renderer.writing_messages,
            "input_line_written": self.terminal_renderer.input_line_written,
            "last_line_count": self.terminal_renderer.last_line_count,
            "conversation_active": self.terminal_renderer.conversation_active,
            "thinking_active": self.terminal_renderer.thinking_active,
        }

    def queue_message(self, message_type: str, content: str, **kwargs) -> None:
        """Queue a message for coordinated display.

        Args:
            message_type: Type of message ("system", "assistant", "user", "error")
            content: Message content to display
            **kwargs: Additional arguments for message formatting
        """
        self.message_queue.append((message_type, content, kwargs))
        logger.debug(f"Queued {message_type} message: {content[:50]}...")

    def display_single_message(
        self, message_type: str, content: str, **kwargs
    ) -> None:
        """Display a single message immediately through coordination.

        This method provides a coordinated way for plugins and other systems
        to display individual messages without bypassing the coordination system.

        Args:
            message_type: Type of message ("system", "assistant", "user", "error")
            content: Message content to display
            **kwargs: Additional arguments for message formatting
        """
        self.display_message_sequence([(message_type, content, kwargs)])

    def display_queued_messages(self) -> None:
        """Display all queued messages in proper atomic sequence.

        This method ensures all queued messages display together
        without interference from other systems.
        """
        if self.is_displaying or not self.message_queue:
            return

        # Skip message display during navigation to prevent render conflicts
        if self.navigation_active:
            logger.debug("Skipping message display: navigation active")
            return

        logger.debug(f"Displaying {len(self.message_queue)} queued messages")

        # Enter atomic display mode
        self.is_displaying = True
        self.terminal_renderer.writing_messages = True

        # Clear active area once before all messages
        self.terminal_renderer.clear_active_area()

        try:
            # Display all messages in sequence
            for message_type, content, kwargs in self.message_queue:
                self._display_single_message(message_type, content, kwargs)

        finally:
            # Exit atomic display mode
            self.terminal_renderer.writing_messages = False
            self.message_queue.clear()
            self.is_displaying = False
            # Reset render state for clean input box rendering
            # This prevents duplicate input boxes when render loop resumes
            self.terminal_renderer.input_line_written = False
            self.terminal_renderer.last_line_count = 0
            self.terminal_renderer.active_area_start_position = None
            self.terminal_renderer.invalidate_render_cache()
            # Ensure cursor is visible and at start of new line for clean render
            self.terminal_renderer.terminal_state.write_raw("\r\033[?25h")
            logger.debug("Completed atomic message display")

    def display_message_sequence(
        self, messages: List[Tuple[str, str, Dict[str, Any]]]
    ) -> None:
        """Display a sequence of messages atomically.

        This is the primary method for coordinated message display.
        All messages in the sequence will display together without
        interference from other systems.

        Args:
            messages: List of (message_type, content, kwargs) tuples

        Example:
            coordinator.display_message_sequence([
                ("system", "Thought for 2.1 seconds", {}),
                ("assistant", "Hello! How can I help you?", {})
            ])
        """
        # Queue all messages
        for message_type, content, kwargs in messages:
            self.queue_message(message_type, content, **kwargs)

        # Display them atomically
        self.display_queued_messages()

    def _display_single_message(
        self, message_type: str, content: str, kwargs: Dict[str, Any]
    ) -> None:
        """Display a single message using the appropriate method.

        Args:
            message_type: Type of message to display
            content: Message content
            kwargs: Additional formatting arguments
        """
        try:
            # Use modern design system for all message rendering
            terminal_state = self.terminal_renderer.terminal_state
            from .terminal_state import TerminalMode

            # Exit raw mode temporarily for clean output
            was_raw = terminal_state.current_mode == TerminalMode.RAW
            if was_raw:
                terminal_state.exit_raw_mode()

            try:
                if message_type == "system":
                    # System/info messages - dimmed style
                    # Apply display filters (plugins can strip their XML commands)
                    filtered_content = DisplayFilterRegistry.apply_filters(
                        content, MessageType.SYSTEM
                    )
                    # Type validation - handle None returns from broken filters
                    if filtered_content is None:
                        logger.warning(f"System message filter returned None, using original content")
                        filtered_content = content
                    # Skip empty content (used for spacing)
                    if not filtered_content.strip():
                        logger.debug(f"System message empty after filtering")
                        return
                    rendered = ModernMessageRenderer.info_block(filtered_content)
                    print(rendered, flush=True)

                elif message_type == "assistant":
                    # Assistant responses - gradient response block
                    # Apply display filters (plugins can strip their XML commands)
                    filtered_content = DisplayFilterRegistry.apply_filters(
                        content, MessageType.ASSISTANT
                    )
                    # Type validation - handle None returns from broken filters
                    if filtered_content is None:
                        logger.warning(f"Assistant message filter returned None, using original content")
                        filtered_content = content
                    # Only render if there's content after filtering
                    if filtered_content.strip():
                        lines = filtered_content.split('\n')
                        rendered = ModernMessageRenderer.response_block(lines)
                        print(rendered, flush=True)
                    else:
                        logger.debug(f"Assistant message empty after filtering")

                elif message_type == "user":
                    # User input echo
                    # Apply display filters (plugins can strip their XML commands)
                    filtered_content = DisplayFilterRegistry.apply_filters(
                        content, MessageType.USER
                    )
                    # Type validation - handle None returns from broken filters
                    if filtered_content is None:
                        logger.warning(f"User message filter returned None, using original content")
                        filtered_content = content
                    # Always render user messages (even if empty after filtering)
                    rendered = ModernMessageRenderer.user_message(filtered_content)
                    print(rendered, flush=True)

                elif message_type == "error":
                    # Error messages - red error block
                    # Apply display filters (plugins can strip their XML commands)
                    filtered_content = DisplayFilterRegistry.apply_filters(
                        content, MessageType.ERROR
                    )
                    # Type validation - handle None returns from broken filters
                    if filtered_content is None:
                        logger.warning(f"Error message filter returned None, using original content")
                        filtered_content = content
                    rendered = ModernMessageRenderer.error_block("Error", filtered_content)
                    print(rendered, flush=True)

                elif message_type == "info":
                    # Info messages
                    # Apply display filters (plugins can strip their XML commands)
                    filtered_content = DisplayFilterRegistry.apply_filters(
                        content, MessageType.INFO
                    )
                    # Type validation - handle None returns from broken filters
                    if filtered_content is None:
                        logger.warning(f"Info message filter returned None, using original content")
                        filtered_content = content
                    # Always render info messages (even if empty after filtering)
                    rendered = ModernMessageRenderer.info_block(filtered_content)
                    print(rendered, flush=True)

                elif message_type == "debug":
                    # Debug messages
                    # Apply display filters (plugins can strip their XML commands)
                    filtered_content = DisplayFilterRegistry.apply_filters(
                        content, MessageType.DEBUG
                    )
                    # Type validation - handle None returns from broken filters
                    if filtered_content is None:
                        logger.warning(f"Debug message filter returned None, using original content")
                        filtered_content = content
                    # Always render debug messages (even if empty after filtering)
                    rendered = ModernMessageRenderer.info_block(filtered_content)
                    print(rendered, flush=True)

                elif message_type == "tool":
                    # Tool calls/results - use proper tool rendering
                    tool_name = kwargs.get("tool_name", "")
                    tool_args = kwargs.get("tool_args", "")
                    tool_status = kwargs.get("tool_status", "success")
                    result_summary = kwargs.get("result_summary", None)
                    if tool_name:
                        rendered = ModernMessageRenderer.tool_call(
                            tool_name, tool_args, tool_status, result_summary=result_summary
                        )
                        print(rendered, flush=True)
                    # Tool result content - only show if there's additional content beyond the summary
                    # (result_summary already appears inline in tool_call above)
                    if content.strip():
                        # Apply display filters (plugins can strip their XML commands)
                        # Use ASSISTANT type since there's no specific TOOL type
                        filtered_content = DisplayFilterRegistry.apply_filters(
                            content, MessageType.ASSISTANT
                        )
                        # Type validation - handle None returns from broken filters
                        if filtered_content is None:
                            logger.warning(f"Tool result filter returned None, using original content")
                            filtered_content = content
                        if filtered_content.strip():
                            result_lines = filtered_content.split('\n')
                            rendered = ModernMessageRenderer.tool_result(result_lines)
                            print(rendered, flush=True)
                        else:
                            logger.debug(f"Tool result empty after filtering")

                else:
                    logger.warning(f"Unknown message type: {message_type}")
                    # Fallback - use assistant style and apply filters
                    filtered_content = DisplayFilterRegistry.apply_filters(
                        content, MessageType.ASSISTANT
                    )
                    # Type validation - handle None returns from broken filters
                    if filtered_content is None:
                        logger.warning(f"Fallback filter returned None, using original content")
                        filtered_content = content
                    rendered = ModernMessageRenderer.assistant_message(filtered_content)
                    print(rendered, flush=True)

            finally:
                # Restore raw mode if it was active
                if was_raw:
                    terminal_state.enter_raw_mode()

        except Exception as e:
            logger.error(f"Error displaying {message_type} message: {e}")
            # Fallback display to prevent total failure
            try:
                print(f"[{message_type.upper()}] {content}")
            except Exception:
                logger.error(
                    "Critical: Failed to display message even with fallback"
                )

    def clear_queue(self) -> None:
        """Clear all queued messages without displaying them."""
        self.message_queue.clear()
        logger.debug("Cleared message queue")

    def get_queue_status(self) -> Dict[str, Any]:
        """Get current queue status for debugging.

        Returns:
            Dictionary with queue information
        """
        return {
            "queue_length": len(self.message_queue),
            "is_displaying": self.is_displaying,
            "queued_types": [msg[0] for msg in self.message_queue],
        }

    # === Buffer Transition Management ===
    # These methods handle state preservation during modal/fullscreen transitions

    def enter_alternate_buffer(self) -> None:
        """Mark entering alternate buffer and pause render loop.

        Call this BEFORE opening a modal or entering fullscreen mode.
        Captures current render state for potential restoration.
        """
        if self._in_alternate_buffer:
            logger.warning("Already in alternate buffer")
            return

        # Capture state BEFORE modifying anything
        self._saved_main_buffer_state = self._capture_render_state()
        logger.debug(f"Captured render state: {self._saved_main_buffer_state}")

        self._in_alternate_buffer = True
        # Prevent render loop interference during modal
        self.terminal_renderer.writing_messages = True
        logger.debug("Entered alternate buffer mode")

    def exit_alternate_buffer(self, restore_state: bool = False) -> None:
        """Exit alternate buffer mode and reset render state.

        Call this AFTER closing a modal or exiting fullscreen mode.

        Args:
            restore_state: If True, restore captured state. If False (default),
                          reset to clean state for fresh input rendering.
        """
        if not self._in_alternate_buffer:
            logger.warning("Not in alternate buffer")
            return

        self._in_alternate_buffer = False

        if restore_state and self._saved_main_buffer_state:
            # Restore previously captured state
            self.terminal_renderer.writing_messages = self._saved_main_buffer_state[
                "writing_messages"
            ]
            self.terminal_renderer.input_line_written = self._saved_main_buffer_state[
                "input_line_written"
            ]
            self.terminal_renderer.last_line_count = self._saved_main_buffer_state[
                "last_line_count"
            ]
            logger.debug(f"Restored render state: {self._saved_main_buffer_state}")
        else:
            # Reset to clean state (default - prevents duplicate input boxes)
            self.terminal_renderer.writing_messages = False
            self.terminal_renderer.input_line_written = False
            self.terminal_renderer.last_line_count = 0
            logger.debug("Reset to clean render state")

        # Always invalidate cache after buffer transition
        self.terminal_renderer.invalidate_render_cache()
        self._saved_main_buffer_state = None

    def get_saved_state(self) -> Optional[Dict[str, Any]]:
        """Get the saved render state (for debugging).

        Returns:
            Saved state dict if in alternate buffer, None otherwise.
        """
        return self._saved_main_buffer_state

    # === Navigation Awareness ===
    # These methods integrate with status navigation system to prevent render conflicts

    def set_navigation_active(self, active: bool) -> None:
        """Set navigation active state (pauses message rendering).

        This is called by StatusNavigationManager to prevent LLM streaming
        and message display from interfering with navigation UI.

        Args:
            active: True when navigation mode is active, False when exiting
        """
        self.navigation_active = active
        logger.debug(f"Navigation active set to: {active}")

    def is_writing_messages(self) -> bool:
        """Check if currently writing messages or navigation is active.

        This provides a unified check for systems that need to know if
        message display is in progress or blocked (e.g., navigation mode).

        Returns:
            True if terminal renderer is writing messages or navigation is active
        """
        return self.terminal_renderer.writing_messages or self.navigation_active
