"""AiCippY Textual TUI — dark-themed interactive agent interface."""

from __future__ import annotations

import asyncio
import platform
import time
from pathlib import Path

from textual import on, work
from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.containers import Horizontal, Vertical, VerticalScroll
from textual.css.query import NoMatches
from textual.reactive import reactive
from textual.widgets import Button, Input, Static

# ---------------------------------------------------------------------------
# Display helpers
# ---------------------------------------------------------------------------

DISPLAY_NAMES: dict[str, str] = {"odin": "OdiN", "arjuna": "ArjunA"}


def _dn(key: str) -> str:
    return DISPLAY_NAMES.get(key.lower(), key) if key else "OdiN"


# ---------------------------------------------------------------------------
# CSS — matching reference dark design
# ---------------------------------------------------------------------------

APP_CSS = """
Screen {
    background: #0d1117;
}

/* ── Header bar ── */
#header-bar {
    dock: top;
    height: 3;
    background: #161b22;
    border-bottom: solid #30363d;
    padding: 0 1;
}
#header-left {
    width: 1fr;
    height: 3;
    content-align: left middle;
    padding: 0 1;
}
#header-right {
    width: auto;
    height: 3;
    content-align: right middle;
    padding: 0 1;
}

/* ── Info bar ── */
#info-bar {
    dock: top;
    height: 1;
    background: #0d1117;
    color: #6b7280;
    padding: 0 2;
}

/* ── Chat area ── */
#chat-scroll {
    background: #0d1117;
    padding: 0 1;
}
.msg-user {
    margin: 1 0 0 6;
    padding: 1 2;
    background: #1a3a5c;
    color: #e5e7eb;
    border: round #264d73;
    max-width: 90%;
}
.msg-agent {
    margin: 1 6 0 0;
    padding: 1 2;
    background: #1c1c2e;
    color: #d1d5db;
    border: round #30363d;
    max-width: 90%;
}
.msg-system {
    margin: 0 0;
    padding: 0 2;
    color: #a855f7;
}
.msg-success {
    color: #00ff88;
    padding: 0 2;
}
.msg-error {
    color: #ef4444;
    padding: 0 2;
}
.msg-info {
    color: #6b7280;
    padding: 0 2;
}
.msg-action {
    margin: 0 2;
    padding: 0 2;
    color: #f59e0b;
}
.msg-timestamp {
    color: #4b5563;
    text-style: italic;
}

/* ── Input area (footer) ── */
#input-area {
    dock: bottom;
    height: auto;
    max-height: 8;
    background: #161b22;
    border-top: solid #30363d;
    padding: 1 1;
}
#input-row {
    height: 3;
    padding: 0 1;
}
#prompt-input {
    width: 1fr;
    border: round #30363d;
    background: #0d1117;
    color: #e5e7eb;
    padding: 0 1;
}
#prompt-input:focus {
    border: round #6366f1;
}
#send-btn {
    width: 8;
    min-width: 8;
    height: 3;
    margin: 0 0 0 1;
    background: #6366f1;
    color: #ffffff;
    text-style: bold;
    border: none;
}
#send-btn:hover {
    background: #4f46e5;
}

/* ── Command chips ── */
#chips-row {
    height: 1;
    padding: 0 2;
    margin: 0 0;
}
.chip {
    width: auto;
    margin: 0 1 0 0;
    padding: 0 1;
    background: #1e293b;
    color: #9ca3af;
    border: round #30363d;
}
.chip:hover {
    background: #334155;
    color: #22d3ee;
}

/* ── Status bar ── */
#status-bar {
    dock: bottom;
    height: 1;
    background: #080b10;
    color: #6b7280;
    padding: 0 2;
}
"""


# ---------------------------------------------------------------------------
# Widgets
# ---------------------------------------------------------------------------


class HeaderBar(Horizontal):
    """Top title bar: brand + agent + active + zoozoo."""

    def __init__(self, model: str = "OdiN", user: str = "") -> None:
        super().__init__(id="header-bar")
        self._model = model
        self._user = user

    def compose(self) -> ComposeResult:
        yield Static(self._render_left(), id="header-left")
        yield Static(self._render_right(), id="header-right")

    def _render_left(self) -> str:
        try:
            from aicippy import __version__
        except Exception:
            __version__ = "3.6"
        brand = "[bold #00ff88]Ai[/][bold #22d3ee]Cipp[/][bold #00ff88]Y[/]"
        return f"{brand} [#6b7280]v{__version__}[/]  Agent: [bold #f59e0b]{self._model}[/]"

    def _render_right(self) -> str:
        parts = ""
        if self._user:
            parts += f"[#22d3ee]{self._user}[/]  "
        parts += "[bold #00ff88]● ACTIVE[/]   [bold #22d3ee]╭◉‿◉╮[/]✨"
        return parts


class InfoBar(Static):
    """Project + CWD info line."""

    def __init__(self) -> None:
        _home = str(Path.home())
        _cwd = str(Path.cwd())
        _short = _cwd.replace(_home, "~", 1) if _cwd.startswith(_home) else _cwd
        _proj = Path(_cwd).name
        markup = f"[#6b7280]project:[/] [bold #22d3ee]{_proj}[/]   [#6b7280]CWD:[/] [bold #ffd93d]{_short}[/]"
        super().__init__(markup, id="info-bar")


class ChatMessage(Static):
    """Single chat message bubble."""

    pass


class CommandChip(Button):
    """Clickable slash command chip."""

    def __init__(self, cmd: str) -> None:
        super().__init__(cmd, classes="chip")
        self._cmd = cmd


class StatusBar(Static):
    """Bottom status bar with tokens/latency/memory/session."""

    tokens = reactive(0)
    latency = reactive(0)
    session_start = reactive(0.0)

    def __init__(self) -> None:
        super().__init__(id="status-bar")
        self.session_start = time.time()

    def render(self) -> str:
        _dur = int(time.time() - self.session_start)
        _m, _s = divmod(_dur, 60)
        _h, _m = divmod(_m, 60)
        _dur_str = f"{_h}h {_m}m" if _h else f"{_m}m {_s}s"
        _lat = f"{self.latency}ms" if self.latency > 0 else "--"
        return (
            f"F1 Help   F3 Settings   Ctrl+L Clear   Ctrl+C Abort"
            f"    ⚡ Tokens: {self.tokens:,}"
            f"   Latency: {_lat}"
            f"   Session: {_dur_str}"
        )


# ---------------------------------------------------------------------------
# Main App
# ---------------------------------------------------------------------------


class AiCippyApp(App):
    """AiCippY Textual TUI Application."""

    TITLE = "AiCippY"
    CSS = APP_CSS

    BINDINGS: list[Binding] = [  # noqa: RUF012
        Binding("ctrl+c", "quit", "Quit", show=False),
        Binding("ctrl+l", "clear_chat", "Clear"),
        Binding("escape", "quit", "Quit", show=False),
    ]

    def __init__(
        self,
        model: str = "odin",
        user_email: str = "",
        initial_prompt: str = "",
    ) -> None:
        super().__init__()
        self._model = _dn(model)
        self._user_email = user_email
        self._initial_prompt = initial_prompt
        self._msg_count = 0
        self._agent_client = None
        self._tokens = 0

    def compose(self) -> ComposeResult:
        yield HeaderBar(model=self._model, user=self._user_email)
        yield InfoBar()

        with VerticalScroll(id="chat-scroll"):
            # System init messages
            _ts = time.strftime("%H:%M:%S")
            _init = f"{self._model} Agent initialized  \\[Llama 4 Maverick 17B]"
            yield ChatMessage(
                f"[#4b5563]{_ts}[/]  [bold #a855f7]●[/] [#a855f7]{_init}[/]",
                classes="msg-system",
            )
            _plat = f"{platform.system()} {platform.machine()}"
            _pyv = platform.python_version()
            yield ChatMessage(
                f"[#4b5563]{_ts}[/]  [#6b7280]├─ Platform: {_plat} | Python {_pyv}[/]",
                classes="msg-info",
            )
            yield ChatMessage(
                f"[#4b5563]{_ts}[/]  [#6b7280]├─ Workspace: {Path.cwd()}[/]",
                classes="msg-info",
            )
            yield ChatMessage(
                f"[#4b5563]{_ts}[/]  [bold #00ff88]✓[/] [#00ff88]Agent streaming ready[/]",
                classes="msg-success",
            )

        # Input area at bottom
        with Vertical(id="input-area"):
            with Horizontal(id="input-row"):
                yield Input(
                    placeholder=f"Ask {self._model} anything...",
                    id="prompt-input",
                )
                yield Button("SEND", id="send-btn", variant="primary")
            with Horizontal(id="chips-row"):
                for cmd in ["/build", "/deploy", "/test", "/status", "/clear"]:
                    yield CommandChip(cmd)

        yield StatusBar()

    def on_mount(self) -> None:
        """Focus input on startup."""
        self.query_one("#prompt-input", Input).focus()
        if self._initial_prompt:
            self.call_after_refresh(self._send_initial)

    def _send_initial(self) -> None:
        inp = self.query_one("#prompt-input", Input)
        inp.value = self._initial_prompt
        self._handle_send()

    @on(Input.Submitted, "#prompt-input")
    def _on_input_submit(self) -> None:
        self._handle_send()

    @on(Button.Pressed, "#send-btn")
    def _on_send_click(self) -> None:
        self._handle_send()

    @on(Button.Pressed, ".chip")
    def _on_chip_click(self, event: Button.Pressed) -> None:
        cmd = event.button.label
        if str(cmd) == "/clear":
            self.action_clear_chat()
        elif str(cmd) == "/status":
            self._add_system_msg("Session active. Type a prompt to interact with the agent.")
        else:
            inp = self.query_one("#prompt-input", Input)
            inp.value = str(cmd) + " "
            inp.focus()

    def _handle_send(self) -> None:
        inp = self.query_one("#prompt-input", Input)
        text = inp.value.strip()
        if not text:
            return
        inp.value = ""

        # Exit commands
        if text.lower() in {"quit", "exit", "q", "/quit", "/exit"}:
            self.exit()
            return

        # Show user message
        self._add_user_msg(text)

        # Send to agent
        self._invoke_agent(text)

    def _add_user_msg(self, text: str) -> None:
        _ts = time.strftime("%H:%M:%S")
        self._msg_count += 1
        scroll = self.query_one("#chat-scroll", VerticalScroll)
        msg = ChatMessage(
            f"[bold #86efac]You ❯[/]  {text}\n[#4b5563 italic]{_ts}[/]",
            classes="msg-user",
        )
        scroll.mount(msg)
        msg.scroll_visible()

    def _add_agent_msg(self, text: str) -> None:
        _ts = time.strftime("%H:%M:%S")
        self._msg_count += 1
        scroll = self.query_one("#chat-scroll", VerticalScroll)
        msg = ChatMessage(
            f"[bold #22d3ee]{self._model} ❯[/]  {text}\n[#4b5563 italic]{_ts}[/]",
            classes="msg-agent",
        )
        scroll.mount(msg)
        msg.scroll_visible()

    def _add_system_msg(self, text: str) -> None:
        _ts = time.strftime("%H:%M:%S")
        scroll = self.query_one("#chat-scroll", VerticalScroll)
        msg = ChatMessage(
            f"[#4b5563]{_ts}[/]  [bold #a855f7]●[/] [#a855f7]{text}[/]",
            classes="msg-system",
        )
        scroll.mount(msg)
        msg.scroll_visible()

    def _add_action_msg(self, text: str) -> None:
        _ts = time.strftime("%H:%M:%S")
        scroll = self.query_one("#chat-scroll", VerticalScroll)
        msg = ChatMessage(
            f"[#4b5563]{_ts}[/]  [bold #f59e0b]⚡[/] [#f59e0b]{text}[/]",
            classes="msg-action",
        )
        scroll.mount(msg)
        msg.scroll_visible()

    @work(thread=True)
    def _invoke_agent(self, text: str) -> None:
        """Call the Bedrock agent in a background thread."""
        try:
            from aicippy.agents.bedrock_agent import BedrockAgentClient

            if self._agent_client is None:
                self._agent_client = BedrockAgentClient()

            self.call_from_thread(
                self._add_system_msg, "Processing..."
            )

            start_t = time.time()
            tokens: list[str] = []

            def on_token(t: str) -> None:
                tokens.append(t)

            # Run async invoke in a new event loop (we're in a thread)
            loop = asyncio.new_event_loop()
            try:
                result = loop.run_until_complete(
                    self._agent_client.invoke(text, on_token=on_token)
                )
            finally:
                loop.close()

            elapsed = int((time.time() - start_t) * 1000)
            self._tokens += len(result.split())

            # Update status bar
            try:
                sb = self.query_one(StatusBar)
                sb.tokens = self._tokens
                sb.latency = elapsed
            except NoMatches:
                pass

            # Show response
            if result:
                self.call_from_thread(self._add_agent_msg, result)
            else:
                raw = "".join(tokens)
                if raw:
                    self.call_from_thread(self._add_agent_msg, raw)
                else:
                    self.call_from_thread(
                        self._add_system_msg, "No response received."
                    )

        except Exception as exc:
            self.call_from_thread(
                self._add_system_msg, f"Error: {exc}"
            )

        # Refocus input
        self.call_from_thread(
            lambda: self.query_one("#prompt-input", Input).focus()
        )

    def action_clear_chat(self) -> None:
        """Clear all chat messages."""
        scroll = self.query_one("#chat-scroll", VerticalScroll)
        scroll.remove_children()
        self._add_system_msg("Chat cleared.")

    def action_quit(self) -> None:
        self.exit()


# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------


async def start_tui_session(
    model: str = "odin",
    user_email: str = "",
    initial_prompt: str = "",
    **_kwargs: object,
) -> None:
    """Launch the Textual TUI app."""
    app = AiCippyApp(
        model=model,
        user_email=user_email,
        initial_prompt=initial_prompt,
    )
    await app.run_async()
