"""
Real-time chat components using HTMX websockets.
"""
from .base import Component
from typing import Callable, Optional


class Chat(Component):
    """Main chat container handling WebSocket connections via HTMX."""
    def __init__(
        self,
        websocket_url: Callable | str,
        topbar: list[Component] = None,
        title: Optional[str] = None,
        messages: Optional[Component] = None,
        message_input: Optional[Component] = None,
        history: Optional[Component] = None,
        **kwargs,
    ):
        super().__init__(**kwargs)
        topbar = topbar or []
        if messages:
            self.children.append(messages)
        if message_input:
            self.children.append(message_input)
        if history:
            self.children.append(history)
        self.children.extend(topbar)

        self.websocket_url = websocket_url
        self.topbar = topbar
        self.messages = messages
        self.message_input = message_input
        self.history = history
        self.title = title

    def get_websocket_url(self, **kwargs) -> str:
        if callable(self.websocket_url):
            return self.websocket_url(kwargs["request"])
        return str(self.websocket_url)

    def render_html(self, **kwargs) -> str:
        websocket_url = self.get_websocket_url(**kwargs)

        top_components = ""
        if len(self.topbar) > 0:
            top_components += '<div class="flex flex-row gap-1 items-center">'
            for top_component in self.topbar:
                top_components += top_component.render(**kwargs)
            top_components += "</div>"

        return f"""
<div id="{self.uid}" hx-ext="ws" ws-connect="{websocket_url}" class="flex flex-col h-full {self.classes}">
    <div class="flex flex-row justify-between items-center w-full p-2 border-b border-base-300 bg-base-200/50">
        {f'<span class="text-sm font-bold uppercase tracking-wider opacity-50 px-2">{self.title}</span>' if self.title else "<span></span>"}
        {top_components}
    </div>
    {self.messages.render(**kwargs) if self.messages else ""}
    {self.message_input.render(**kwargs) if self.message_input else ""}
</div>
"""


class ChatButton(Component):
    def __init__(
        self,
        title: str,
        icon: str,
        **kwargs
    ):
        super().__init__(**kwargs)
        self.title = title
        self.icon = icon

    def render_html(self, **kwargs) -> str:
        value = self.get_value(**kwargs)
        type_input = (
            ""
            if self.key == "type"
            else '<input name="type" readonly class="hidden" value="button">'
        )
        return f"""
<form id="{self.uid}" ws-send>
    <input name="{self.key}" readonly class="hidden" value="{value}">
    {type_input}
    <button type="submit" class="btn btn-square btn-sm p-1.5 {self.classes}" title="{self.title}">{{% heroicon_mini "{self.icon}" %}}</button>
</form>
"""


class MessageLoader(Component):
    def __init__(
        self,
        offset: int,
        count: int = 10,
        **kwargs
    ):
        super().__init__(**kwargs)
        self.offset = offset
        self.count = count

    def render_html(self, **kwargs) -> str:
        return f"""
<form id="{self.uid}" ws-send hx-trigger="intersect once" class="{self.classes}">
    <input name="type" readonly class="hidden" value="load_more">
    <input name="offset" readonly class="hidden" value="{self.offset}">
    <input name="count" readonly class="hidden" value="{self.count}">
    Loading...
</form>
"""


class MessageInput(Component):
    def __init__(
        self,
        disabled: bool = False,
        visible: bool = True,
        generating: bool = False,
        **kwargs
    ):
        super().__init__(**kwargs)
        self.disabled = disabled
        self.visible = visible
        self.generating = generating

    def render_html(self, **kwargs) -> str:
        # Allow kwargs to override instance attributes
        visible = kwargs.get("visible", self.visible)
        generating = kwargs.get("generating", self.generating)
        disabled = kwargs.get("disabled", self.disabled)

        hidden_class = "" if visible else "hidden"
        disabled_attr = "disabled" if disabled or generating else ""

        stop_button = ""
        send_button = f"""
<button type="submit" class="btn btn-primary btn-square" :disabled="!input.trim()">
    {{% heroicon_mini "paper-airplane" %}}
</button>
"""
        if generating:
            send_button = ""
            stop_button = f"""
<form ws-send class="flex-none">
    <input name="type" readonly class="hidden" value="stop_generation">
    <button type="submit" class="btn btn-error btn-square">
        {{% heroicon_mini "stop" %}}
    </button>
</form>
"""

        return f"""
<div id="{self.uid}" class="p-4 border-t border-base-300 {hidden_class} {self.classes}">
    <div class="flex gap-2 w-full items-end">
        <form id="{self.uid}-form" ws-send x-data="{{ input: '' }}" @htmx:ws-after-send="input = ''"
            class="flex-1 flex gap-2 w-full items-end">
            <input name="type" readonly class="hidden" value="message">
            <textarea x-model="input" data-bind="input" name="content" placeholder="Type a message..."
                class="textarea textarea-bordered flex-1 h-24" {disabled_attr}
                @keydown.enter="if (!$event.shiftKey && input.trim()) {{ $event.preventDefault(); $el.closest('form').requestSubmit(); }}"></textarea>
            {send_button}
        </form>
        {stop_button}
    </div>
</div>
"""


class MessagesContainer(Component):
    def __init__(
        self,
        autoscroll: bool = True,
        **kwargs
    ):
        super().__init__(**kwargs)
        self.autoscroll = autoscroll

    def render_messages(self, **kwargs) -> str:
        """Override in subclasses to provide message rendering logic."""
        return kwargs.get("messages_html", "")

    def render_html(self, **kwargs) -> str:
        autoscroll = kwargs.get("autoscroll", self.autoscroll)
        prefix_html = kwargs.get("prefix_html", "")

        messages_html = self.render_messages(**kwargs)

        autoscroll_script = ""
        if autoscroll:
            autoscroll_script = """
x-data="{ autoScroll: true }"
x-init="
    const observer = new MutationObserver(() => {
        if (autoScroll) $el.scrollTop = $el.scrollHeight;
    });
    observer.observe($el, { childList: true, subtree: true });
    $el.scrollTop = $el.scrollHeight;
"
@scroll="autoScroll = ($el.scrollHeight - $el.scrollTop - $el.clientHeight) < 50"
"""

        return f"""
<div id="{self.uid}" class="flex-1 overflow-y-auto {self.classes}" {autoscroll_script}>
    {prefix_html}
    {messages_html}
</div>
"""


class EmptyChat(Component):
    def __init__(
        self,
        message: str = "Start a conversation",
        **kwargs
    ):
        super().__init__(**kwargs)
        self.message = message

    def render_html(self, **kwargs) -> str:
        message = kwargs.get("message", self.message)
        return f"""
<div id="{self.uid}" class="text-center text-base-content/50 py-8 {self.classes}">
    {message}
</div>
"""


class OOBAppend(Component):
    def __init__(self, target: str = "", child: str = "", **kwargs):
        super().__init__(**kwargs)
        self.target = target

    def render_html(self, **kwargs) -> str:
        target = kwargs.get("target", self.target)
        children = kwargs.get("children", self.children)
        return f'<div hx-swap-oob="beforeend:{target}">{children}</div>'


class OOBReplace(Component):
    def __init__(self, target: str = "", **kwargs):
        super().__init__(**kwargs)
        self.target = target

    def render_html(self, **kwargs) -> str:
        target = kwargs.get("target", self.target)
        children = kwargs.get("children", self.children)
        return f'<div hx-swap-oob="outerHTML:{target}">{children}</div>'


class OOBDelete(Component):
    def __init__(self, target: str = "", **kwargs):
        super().__init__(**kwargs)
        self.target = target

    def render_html(self, **kwargs) -> str:
        target = kwargs.get("target", self.target)
        return f'<div hx-swap-oob="delete:{target}"></div>'


class OOBUpdate(Component):
    def __init__(self, id: str = "", child: str = "", **kwargs):
        super().__init__(**kwargs)
        self.id = id
        self.child = child

    def render_html(self, **kwargs) -> str:
        id = kwargs.get("id", self.id)
        child = kwargs.get("child", self.child)
        return f'<div id="{id}" hx-swap-oob="true">{child}</div>'


class StreamingToken(Component):
    def __init__(self, target: str = "", content: str = "", **kwargs):
        super().__init__(**kwargs)
        self.target = target
        self.content = content

    def render_html(self, **kwargs) -> str:
        target = kwargs.get("target", self.target)
        content = kwargs.get("content", self.content)
        # Convert newlines to <br> for HTML rendering
        safe_content = content.replace("\n", "<br>")
        return f'<span hx-swap-oob="beforeend:{target}">{safe_content}</span>'


class Script(Component):
    def __init__(self, code: str = "", **kwargs):
        super().__init__(**kwargs)
        self.code = code

    def render_html(self, **kwargs) -> str:
        code = kwargs.get("code", self.code)
        return f"<script>{code}</script>"


class Toast(Component):
    def __init__(
        self,
        message: str = "",
        type: str = "error",
        **kwargs
    ):
        super().__init__(**kwargs)
        self.message = message
        self.type = type

    def render_html(self, **kwargs) -> str:
        message = kwargs.get("message", self.message)
        toast_type = kwargs.get("type", self.type)
        return f"""
<div id="{self.uid}" class="toast toast-top toast-end {self.classes}">
    <div class="alert alert-{toast_type}">
        <span>{message}</span>
    </div>
</div>
"""
