from __future__ import annotations

import argparse
import os
from datetime import UTC, datetime
from typing import Any

from textual.app import App, ComposeResult
from textual.containers import Horizontal
from textual.widgets import Header, RichLog, Static

from relay.tui.api_client import ApiClient
from relay.tui.injector import TmuxInjector
from relay.tui.sse_client import SSEListener


class MembersPanel(Static):
    DEFAULT_CSS = """
    MembersPanel {
        width: 25;
        border-left: solid $accent;
        padding: 0 1;
    }
    """


class StatusBar(Static):
    DEFAULT_CSS = """
    StatusBar {
        dock: bottom;
        height: 1;
        background: $surface;
        color: $text-muted;
        padding: 0 1;
    }
    """


class RelayTUI(App[None]):
    TITLE = "Relay"

    CSS = """
    Screen {
        layout: vertical;
    }

    #main-area {
        height: 1fr;
    }

    #chat-log {
        width: 1fr;
        border: solid $accent;
        scrollbar-size: 1 1;
    }
    """

    BINDINGS = [
        ("q", "quit", "Quit"),
    ]

    def __init__(
        self,
        server_url: str,
        group_id: str,
        peer_id: str,
        target_pane: str,
        groups_header: str = "",
    ) -> None:
        super().__init__()
        self.server_url = server_url
        self.group_id = group_id
        self.peer_id = peer_id
        self.target_pane = target_pane
        self.all_groups = (
            [pair.split(":")[0] for pair in groups_header.split(",") if pair]
            if groups_header
            else [group_id]
        )
        self.listener = SSEListener(
            server_url=server_url,
            group_id=group_id,
            peer_id=peer_id,
            on_event=self.handle_event,
            groups_header=groups_header,
        )
        self.injector = TmuxInjector(target_pane=target_pane)
        self.api = ApiClient(
            server_url=server_url,
            groups_header=groups_header,
            peer_id=peer_id,
        )
        self._last_seq: dict[str, int] = {g: 0 for g in self.all_groups}

    def compose(self) -> ComposeResult:
        yield Header(show_clock=True)
        with Horizontal(id="main-area"):
            yield RichLog(highlight=True, markup=True, wrap=True, auto_scroll=True, id="chat-log")
            yield MembersPanel("Loading...", id="members-panel")
        yield StatusBar("Connecting...", id="status-bar")

    async def on_mount(self) -> None:
        groups_label = ", ".join(self.all_groups)
        self.title = f"Also — {groups_label}"
        self.sub_title = ""
        await self.listener.start()
        await self._poll_messages(inject=False)
        await self._poll_peers()
        self.set_interval(3.0, self._poll_messages)
        self.set_interval(10.0, self._poll_peers)
        self.set_interval(2.0, self._update_status)

    async def on_unmount(self) -> None:
        await self.listener.stop()
        await self.api.close()

    async def handle_event(self, event: dict[str, Any]) -> None:
        event_type = event.get("type", "unknown")

        if event_type == "message":
            await self._poll_messages()

        elif event_type in ("peer_joined", "peer_left"):
            log = self.query_one("#chat-log", RichLog)
            timestamp = datetime.now(UTC).strftime("%H:%M:%S")
            peer_display = event.get("display_name", event.get("peer_id", "unknown"))
            group = event.get("group_id", self.group_id)
            label = "[bold yellow]JOIN[/]" if event_type == "peer_joined" else "[bold red]LEFT[/]"
            log.write(f"[dim]{timestamp}[/] [bold green]{group}[/] {label} {peer_display}")
            await self._poll_peers()

    async def _poll_messages(self, inject: bool = True) -> None:
        log = self.query_one("#chat-log", RichLog)
        notifications: list[tuple[str, str]] = []
        for group_id in self.all_groups:
            try:
                messages = await self.api.get_messages(group_id, self._last_seq[group_id])
            except Exception:
                continue
            for msg in messages:
                ts = msg.get("timestamp", "")
                try:
                    dt = datetime.fromisoformat(ts)
                    time_str = dt.strftime("%H:%M:%S")
                except (ValueError, TypeError):
                    time_str = "??:??:??"
                sender = msg.get("from_display", "unknown")
                content = msg.get("content", "")
                log.write(
                    f"[dim]{time_str}[/] [bold green]{group_id}[/] [bold]{sender}[/]: {content}"
                )
                seq = msg.get("seq", 0)
                if seq > self._last_seq[group_id]:
                    self._last_seq[group_id] = seq
                if inject and msg.get("from_peer") != self.peer_id:
                    notifications.append((sender, group_id))
        for sender, group_id in notifications:
            await self.injector.inject_notification(
                from_display=sender,
                group_id=group_id,
            )

    async def _poll_peers(self) -> None:
        panel = self.query_one("#members-panel", MembersPanel)
        lines: list[str] = []
        for group_id in self.all_groups:
            try:
                peers = await self.api.get_peers(group_id)
            except Exception:
                continue
            if len(self.all_groups) > 1:
                lines.append(f"[bold underline]{group_id}[/]")
            for p in peers:
                name = p.get("display_name", p.get("peer_id", "?"))
                if p.get("online"):
                    lines.append(f"  [green]{name} ●[/]")
                else:
                    lines.append(f"  [red]{name} ○[/]")
            if len(self.all_groups) > 1:
                lines.append("")
        panel.update("\n".join(lines) if lines else "[dim]No members[/]")

    async def _update_status(self) -> None:
        status = self.query_one("#status-bar", StatusBar)
        groups_label = ", ".join(self.all_groups)
        if self.listener.connected:
            status.update(f"[green]●[/] {groups_label}")
        else:
            status.update(f"[red]● Reconnecting...[/]")


def main() -> None:
    parser = argparse.ArgumentParser(description="Relay TUI")
    parser.add_argument("--server-url", required=True)
    parser.add_argument("--group-id", required=True)
    parser.add_argument("--peer-id", required=True)
    parser.add_argument("--target-pane", required=True)
    parser.add_argument("--groups-header", default="")
    args = parser.parse_args()

    groups_header = args.groups_header or os.environ.get("ALSO_GROUPS_HEADER", "")

    app = RelayTUI(
        server_url=args.server_url,
        group_id=args.group_id,
        peer_id=args.peer_id,
        target_pane=args.target_pane,
        groups_header=groups_header,
    )
    app.run()


if __name__ == "__main__":
    main()
