"""Model invocation screen for the ALiCE TUI — streaming via converse_stream."""

from __future__ import annotations

from typing import TYPE_CHECKING

from textual.containers import Vertical
from textual.widgets import Button, Footer, Input, Label, TextArea

from alice_cli.constants import DEFAULT_MAX_TOKENS
from alice_cli.models import DEFAULT_MODEL_ALIAS, MODEL_ALIASES, resolve_model_id
from alice_cli.tui.screens.base import AliceScreen
from alice_cli.tui.widgets.breadcrumb import BreadcrumbBar
from alice_cli.tui.widgets.clock import TerminalClock
from alice_cli.tui.widgets.header_bar import AliceHeaderBar
from alice_cli.tui.widgets.output import OutputPanel

if TYPE_CHECKING:
    from textual.app import ComposeResult

_DEFAULT_MODEL_ID = DEFAULT_MODEL_ALIAS


class InvokeScreen(AliceScreen):
    """Interactive Bedrock model invocation screen with streaming output."""

    def __init__(self) -> None:
        super().__init__()
        self._streaming: bool = False

    def compose(self) -> ComposeResult:
        alias_hint = "  ".join(f"{m.alias}={m.description}" for m in MODEL_ALIASES)
        yield AliceHeaderBar()
        yield BreadcrumbBar(id="breadcrumb")
        if self._wordstar:
            from alice_cli.tui.widgets.wordstar_bar import WordStarBar

            yield WordStarBar(id="ws-bar-invoke")
        with Vertical():
            yield Label("Invoke Bedrock Model", id="invoke-title")
            yield Label(f"Model (alias or full ID):  {alias_hint}")
            yield Input(
                value=_DEFAULT_MODEL_ID,
                placeholder="e.g. opus, haiku, sonnet, or full model ID",
                id="model-id-input",
            )

            yield Label("Prompt:")
            yield TextArea(id="prompt-input")
            yield Button("Send", id="btn-invoke", variant="primary")
            yield OutputPanel(id="output-panel", wordstar=self._wordstar)
        yield TerminalClock()
        yield Footer()

    def on_mount(self) -> None:
        """Set model input default from persisted TUI state."""
        state = getattr(self.app, "tui_state", None)
        if state and state.last_model:
            self.query_one("#model-id-input", Input).value = state.last_model

    def on_button_pressed(self, event: Button.Pressed) -> None:
        """Handle the Invoke button press."""
        if event.button.id != "btn-invoke":
            return
        self._invoke_model()

    def _invoke_model(self) -> None:
        """Validate inputs, show waiting indicator, and launch streaming worker."""
        if self._streaming:
            return

        model_input = self.query_one("#model-id-input", Input)
        prompt_area = self.query_one("#prompt-input", TextArea)
        output = self.query_one("#output-panel", OutputPanel)

        model_id = resolve_model_id(model_input.value.strip())
        prompt_text = prompt_area.text.strip()

        output.clear()

        if not prompt_text:
            output.write("[#FF6600]Error: Please enter a prompt.[/]")
            return

        if not model_id:
            output.write("[#FF6600]Error: Please enter a model ID.[/]")
            return

        # Update WordStar bar if present
        if self._wordstar:
            try:
                from alice_cli.tui.widgets.wordstar_bar import WordStarBar

                bar = self.query_one("#ws-bar-invoke", WordStarBar)
                bar.document = model_id[:16].upper()
            except Exception:
                pass

        # Show pulsing waiting indicator (task 10.2)
        output.write("[dim]Waiting for response…[/dim]")

        # Persist the model alias for next time
        state = getattr(self.app, "tui_state", None)
        if state:
            state.last_model = model_input.value.strip()
        self._save_prompt("invoke", prompt_text)

        # Launch streaming in a worker thread (task 10.1)
        self._streaming = True
        self.run_worker(
            lambda: self._stream_response(model_id, prompt_text),
            thread=True,
        )

    def _stream_response(self, model_id: str, prompt_text: str) -> None:
        """Worker thread: call converse_stream and push chunks to the OutputPanel."""
        try:
            session = self._create_session()
            client = session.client("bedrock-runtime")

            response = client.converse_stream(
                modelId=model_id,
                messages=[
                    {
                        "role": "user",
                        "content": [{"text": prompt_text}],
                    }
                ],
                inferenceConfig={"maxTokens": DEFAULT_MAX_TOKENS},
            )

            # Clear the "Waiting for response…" indicator before first token
            self.app.call_from_thread(self._output_clear)

            input_tokens = 0
            output_tokens = 0

            for event in response.get("stream", []):
                if "contentBlockDelta" in event:
                    delta = event["contentBlockDelta"].get("delta", {})
                    if "text" in delta:
                        self.app.call_from_thread(self._append_text, delta["text"])

                # Extract token usage from metadata event
                if "metadata" in event:
                    usage = event["metadata"].get("usage", {})
                    input_tokens = usage.get("inputTokens", 0)
                    output_tokens = usage.get("outputTokens", 0)

            # Display token usage summary below response (task 10.3)
            self.app.call_from_thread(self._show_token_summary, input_tokens, output_tokens)

            # Update TokenTracker widget if present (task 10.4)
            self.app.call_from_thread(self._update_token_tracker, input_tokens, output_tokens)

        except Exception as exc:
            output = self.query_one("#output-panel", OutputPanel)
            self.app.call_from_thread(self._handle_error, exc, output)
        finally:
            self._streaming = False

    # ── Thread-safe UI helpers ────────────────────────────────────────────

    def _output_clear(self) -> None:
        """Clear the output panel (called from worker via call_from_thread)."""
        self.query_one("#output-panel", OutputPanel).clear()

    def _append_text(self, text: str) -> None:
        """Append a text chunk to the output panel (called from worker)."""
        self.query_one("#output-panel", OutputPanel).write(text)

    def _show_token_summary(self, input_tokens: int, output_tokens: int) -> None:
        """Display token usage summary below the response."""
        total = input_tokens + output_tokens
        summary = (
            f"\n[dim]─── tokens: ↑{input_tokens:,} input  "
            f"↓{output_tokens:,} output  Σ{total:,} total ───[/dim]"
        )
        self.query_one("#output-panel", OutputPanel).write(summary)

    # _update_token_tracker is inherited from AliceScreen
