"""Scrollable conversation log widget for the ALiCE TUI.

Renders user and assistant messages as visually distinct styled blocks
with Markdown support for assistant responses.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING, Literal

from textual.containers import VerticalScroll
from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import Markdown, Static

if TYPE_CHECKING:
    from textual.app import ComposeResult


@dataclass
class _ChatMessage:
    """Internal representation of a single chat message."""

    role: Literal["user", "assistant"]
    text: str = ""


class _UserBubble(Static):
    """Right-aligned user message bubble."""

    DEFAULT_CSS = """
    _UserBubble {
        width: 100%;
        background: #2A1F00;
        color: #FFD866;
        padding: 1 2;
        margin: 1 0 0 8;
        text-align: right;
    }
    """


class _AssistantBubble(Widget):
    """Left-aligned assistant message bubble with Markdown rendering."""

    DEFAULT_CSS = """
    _AssistantBubble {
        width: 100%;
        background: #0A1628;
        color: #FFB000;
        padding: 1 2;
        margin: 1 8 0 0;
        height: auto;
    }

    _AssistantBubble Markdown {
        background: #0A1628;
        color: #FFB000;
        margin: 0;
        padding: 0;
    }
    """

    content: reactive[str] = reactive("", always_update=True)

    def __init__(self, text: str = "") -> None:
        super().__init__()
        self.content = text

    def compose(self) -> ComposeResult:
        yield Markdown(self.content, id="md")

    def watch_content(self, value: str) -> None:
        """Update the Markdown widget when content changes."""
        try:
            md = self.query_one("#md", Markdown)
            md.update(value)
        except Exception:  # noqa: BLE001
            pass  # Widget not yet mounted


class ChatLog(Widget):
    """Scrollable conversation log showing user and assistant messages.

    User messages are right-aligned with a warm amber background.
    Assistant messages are left-aligned with a dark blue background
    and Markdown rendering.

    Key methods
    -----------
    add_user_message(text)
        Append a user message bubble.
    add_assistant_message(text)
        Append an assistant message bubble.
    append_to_last(text)
        Append text to the last message (for streaming).
    clear_messages()
        Remove all messages from the log.
    """

    DEFAULT_CSS = """
    ChatLog {
        width: 100%;
        height: 1fr;
        background: #1A1100;
    }

    ChatLog > VerticalScroll {
        width: 100%;
        height: 100%;
        background: #1A1100;
    }
    """

    def __init__(self, **kwargs: object) -> None:
        super().__init__(**kwargs)
        self._messages: list[_ChatMessage] = []

    def compose(self) -> ComposeResult:
        yield VerticalScroll(id="chat-scroll")

    def add_user_message(self, text: str) -> None:
        """Add a user message bubble to the log."""
        msg = _ChatMessage(role="user", text=text)
        self._messages.append(msg)
        scroll = self.query_one("#chat-scroll", VerticalScroll)
        bubble = _UserBubble(text)
        scroll.mount(bubble)
        bubble.scroll_visible()

    def add_assistant_message(self, text: str) -> None:
        """Add an assistant message bubble to the log."""
        msg = _ChatMessage(role="assistant", text=text)
        self._messages.append(msg)
        scroll = self.query_one("#chat-scroll", VerticalScroll)
        bubble = _AssistantBubble(text)
        scroll.mount(bubble)
        bubble.scroll_visible()

    def append_to_last(self, text: str) -> None:
        """Append text to the last message (for streaming responses).

        If there are no messages, this creates a new assistant message.
        """
        if not self._messages:
            self.add_assistant_message(text)
            return

        last = self._messages[-1]
        last.text += text

        scroll = self.query_one("#chat-scroll", VerticalScroll)
        children = list(scroll.children)
        if not children:
            self.add_assistant_message(text)
            return

        last_widget = children[-1]
        if isinstance(last_widget, _AssistantBubble):
            last_widget.content = last.text
        elif isinstance(last_widget, _UserBubble):
            # Last message was from user — start a new assistant message
            self.add_assistant_message(text)

    def clear_messages(self) -> None:
        """Remove all messages from the log."""
        self._messages.clear()
        scroll = self.query_one("#chat-scroll", VerticalScroll)
        scroll.remove_children()

    @property
    def message_count(self) -> int:
        """Return the number of messages in the log."""
        return len(self._messages)
