"""
abi_core.tui.app — Composable Textual dashboard for ABI projects.

Usage (direct):
    from abi_core.tui import AbiConsoleApp
    app = AbiConsoleApp(
        project_name="My Swarm",
        project_version="1.9.25",
        orchestrator_url="http://localhost:8000",
        ollama_host="http://localhost:11434",
    )
    app.run()

Usage (extend):
    class MyConsole(AbiConsoleApp):
        def compose(self):
            yield from super().compose()
            yield MyCustomWidget()
"""

from __future__ import annotations

from textual.app import App, ComposeResult
from textual.binding import Binding
from textual import work

from .widgets import (
    BannerWidget,
    ServiceTable,
    LogStream,
    ConversationPanel,
    CommandInput,
)
from .services import DockerService, OllamaService, OrchestratorClient


class AbiConsoleApp(App):
    """Base interactive console for any ABI project."""

    TITLE = "ABI Console"

    DEFAULT_CSS = """
    Screen {
        layout: grid;
        grid-size: 2 4;
        grid-columns: 2fr 1fr;
        grid-rows: auto auto 1fr 3;
    }

    #banner {
        column-span: 1;
        height: auto;
        max-height: 8;
        padding: 0 1;
        background: $surface;
        border: solid $primary;
    }

    #conversation {
        column-span: 1;
        row-span: 2;
        height: 100%;
        min-height: 8;
        background: $surface;
        border: solid $accent;
    }

    #services {
        column-span: 1;
        height: auto;
        max-height: 14;
        background: $surface;
        border: solid $secondary;
    }

    #log-stream {
        column-span: 2;
        height: 1fr;
        min-height: 6;
        background: $surface;
        border: solid $primary;
    }

    #cmd-input {
        column-span: 2;
        height: 3;
    }

    DataTable > .datatable--header {
        background: $primary-darken-2;
        text-style: bold;
    }
    """

    BINDINGS = [
        Binding("ctrl+c", "quit", "Quit", show=True),
        Binding("ctrl+l", "clear_logs", "Clear Logs", show=True),
        Binding("ctrl+r", "refresh_status", "Refresh", show=True),
    ]

    def __init__(
        self,
        project_name: str = "ABI Project",
        project_version: str = "",
        orchestrator_url: str = "http://localhost:8000",
        ollama_host: str = "http://localhost:11434",
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.project_name = project_name
        self.project_version = project_version

        # Backend services — usable independently
        self.docker_svc = DockerService()
        self.ollama_svc = OllamaService(host=ollama_host)
        self.orchestrator = OrchestratorClient(url=orchestrator_url)

    # ── Layout ───────────────────────────────────────────────────

    def compose(self) -> ComposeResult:
        subtitle = f"OSS CLI v{self.project_version}" if self.project_version else ""
        yield BannerWidget(
            title=f"{self.project_name} — Interactive Console",
            subtitle=subtitle,
            id="banner",
        )
        yield ConversationPanel(id="conversation", markup=True, wrap=True)
        yield ServiceTable(id="services")
        yield LogStream(id="log-stream", markup=True, wrap=True)
        yield CommandInput(id="cmd-input")

    # ── Lifecycle ─────────────────────────────────────────────────

    def on_mount(self) -> None:
        self._refresh_all()
        self.set_interval(10, self._refresh_all)

    def _refresh_all(self) -> None:
        """Refresh banner + service table."""
        services = self.docker_svc.list_services()
        running = sum(1 for s in services if s.get("status") == "running")

        banner: BannerWidget = self.query_one("#banner", BannerWidget)
        banner.refresh_banner(node_count=running)

        table: ServiceTable = self.query_one("#services", ServiceTable)
        table.refresh_services(services)

    # ── Actions ──────────────────────────────────────────────────

    def action_clear_logs(self) -> None:
        self.query_one("#log-stream", LogStream).clear()

    def action_refresh_status(self) -> None:
        self._refresh_all()

    # ── Command handling ─────────────────────────────────────────

    def on_command_input_command_submitted(
        self, event: CommandInput.CommandSubmitted
    ) -> None:
        text = event.value
        log: LogStream = self.query_one("#log-stream", LogStream)
        conv: ConversationPanel = self.query_one("#conversation", ConversationPanel)

        parts = text.split(None, 1)
        cmd = parts[0].lower() if parts else ""
        arg = parts[1] if len(parts) > 1 else ""

        if cmd == "status":
            self._refresh_all()
            log.append_log("[dim]Status refreshed[/]")

        elif cmd == "models":
            self._handle_models(log)

        elif cmd == "ephemeral":
            self._handle_ephemeral(arg, log)

        elif cmd == "logs":
            if arg:
                self._handle_logs(arg, log)
            else:
                log.append_log("[yellow]Usage: logs <container-name>[/]")

        elif cmd == "ask":
            if arg:
                conv.add_user_message(arg)
                self._handle_ask(arg, conv, log)
            else:
                log.append_log("[yellow]Usage: ask <your question>[/]")

        elif cmd == "cleanup":
            n = self.docker_svc.cleanup_ephemeral()
            log.append_log(f"🗑️ Removed {n} stopped ephemeral containers")

        elif cmd == "help":
            log.append_log(
                "[bold]Commands:[/] status, ask <msg>, logs <name>, "
                "models, ephemeral, cleanup, help, quit"
            )

        elif cmd in ("quit", "exit"):
            self.exit()

        else:
            # Default: treat as ask
            conv.add_user_message(text)
            self._handle_ask(text, conv, log)

    # ── Workers (async background tasks) ─────────────────────────

    @work(exclusive=True, group="models")
    async def _handle_models(self, log: LogStream) -> None:
        models = await self.ollama_svc.list_models()
        if not models:
            log.append_log("[dim]No models found or Ollama unreachable[/]")
            return
        for m in models:
            log.append_log(f"  {m['name']}  ({m['size']})")

    @work(exclusive=True, group="ephemeral")
    async def _handle_ephemeral(self, arg: str, log: LogStream) -> None:
        ephemerals = self.docker_svc.list_ephemeral()
        if not ephemerals:
            log.append_log("[dim]No ephemeral containers[/]")
            return
        for e in ephemerals:
            log.append_log(f"  {e['name']}  [{e['status']}]")

    @work(exclusive=True, group="logs")
    async def _handle_logs(self, name: str, log: LogStream) -> None:
        log.append_log(f"[dim]Streaming logs from {name}...[/]")
        async for line in self.docker_svc.stream_logs(name, tail=30):
            log.append_log(line)

    @work(exclusive=True, group="ask")
    async def _handle_ask(
        self, query: str, conv: ConversationPanel, log: LogStream
    ) -> None:
        conv.add_system_message("⏳ Sending to orchestrator...")
        async for event in self.orchestrator.ask(query):
            evt_type = event.get("event", "")
            data = event.get("data", "")

            if evt_type == "done":
                conv.add_system_message("✅ Done")
                break
            elif evt_type == "error":
                conv.add_system_message(f"[red]❌ {data}[/]")
                break
            else:
                # Stream event data to both panels
                if isinstance(data, dict):
                    # Show meaningful fields
                    msg = data.get("message", "") or data.get("text", "")
                    if msg:
                        conv.add_event(msg)
                    # Always log raw to log panel
                    log.append_log(f"[dim]{data}[/]")
                else:
                    conv.add_event(str(data))


# ── Standalone entry point ───────────────────────────────────────

def run_console(
    project_name: str = "ABI Swarm",
    project_version: str = "",
    orchestrator_url: str = "http://localhost:8000",
    ollama_host: str = "http://localhost:11434",
) -> None:
    """Launch the console app. Can be called from any project's main."""
    app = AbiConsoleApp(
        project_name=project_name,
        project_version=project_version,
        orchestrator_url=orchestrator_url,
        ollama_host=ollama_host,
    )
    app.run()
