"""ALiCE TUI — retro amber terminal interface."""

from __future__ import annotations

import json
import shutil
import subprocess
from dataclasses import asdict, dataclass, field
from pathlib import Path
from typing import TYPE_CHECKING, Any

from textual.app import App
from textual.binding import Binding

from alice_cli.personality import HINT_FONT, RECOMMENDED_FONTS
from alice_cli.tui.screens.home import HomeScreen

if TYPE_CHECKING:
    from alice_cli.models import CLIConfig

_STATE_PATH = Path.home() / ".alice" / "tui_state.json"


@dataclass
class TUIState:
    """Persistent TUI session state."""

    last_model: str = "sonnet"
    last_screen: str = "home"
    theme: str = "amber"
    token_dashboard_visible: bool = True
    prompt_history: dict[str, list[str]] = field(default_factory=dict)
    session_input_tokens: int = 0
    session_output_tokens: int = 0


def load_tui_state() -> TUIState:
    """Load state from ~/.alice/tui_state.json, returning defaults on any error."""
    try:
        data = json.loads(_STATE_PATH.read_text(encoding="utf-8"))
        if not isinstance(data, dict):
            return TUIState()
        # Only pass known fields to the constructor
        known_fields = {f.name for f in TUIState.__dataclass_fields__.values()}
        filtered = {k: v for k, v in data.items() if k in known_fields}
        return TUIState(**filtered)
    except Exception:  # noqa: BLE001
        return TUIState()


def save_tui_state(state: TUIState) -> None:
    """Persist state to ~/.alice/tui_state.json."""
    _STATE_PATH.parent.mkdir(parents=True, exist_ok=True)
    _STATE_PATH.write_text(
        json.dumps(asdict(state), indent=2) + "\n",
        encoding="utf-8",
    )


def _detect_terminal_font() -> str | None:
    """Best-effort detection of the current terminal font on macOS.

    Returns the font family name if it can be determined, otherwise ``None``.
    Works for iTerm2 (via its AppleScript API) and Terminal.app (via defaults).
    """
    # iTerm2
    osascript = shutil.which("osascript")
    if osascript:
        try:
            result = subprocess.run(
                [
                    osascript,
                    "-e",
                    'tell application "iTerm2" to get the name of the font '
                    "of current session of current window",
                ],
                capture_output=True,
                text=True,
                timeout=2,
            )
            if result.returncode == 0 and result.stdout.strip():
                return result.stdout.strip()
        except (subprocess.TimeoutExpired, OSError):
            pass

    # Terminal.app
    defaults = shutil.which("defaults")
    if defaults:
        try:
            result = subprocess.run(
                [defaults, "read", "com.apple.Terminal", "Default Window Settings"],
                capture_output=True,
                text=True,
                timeout=2,
            )
            profile = result.stdout.strip() if result.returncode == 0 else "Basic"
            result = subprocess.run(
                [
                    defaults,
                    "read",
                    "com.apple.Terminal",
                    f"Window Settings.{profile}.Font",
                ],
                capture_output=True,
                text=True,
                timeout=2,
            )
            if result.returncode == 0 and result.stdout.strip():
                return result.stdout.strip()
        except (subprocess.TimeoutExpired, OSError):
            pass

    return None


class AliceTUIApp(App[None]):
    """ALiCE TUI — retro amber terminal interface."""

    CSS_PATH = "theme.tcss"
    TITLE = "ALiCE · JH DRCC"
    BINDINGS = [
        Binding("q", "quit", "Quit"),
        Binding("escape", "pop_screen", "Back"),
        Binding("ctrl+t", "toggle_theme", "Theme", show=False),
        Binding("slash", "command_palette", "Search", show=False),
        Binding("question_mark", "help", "Help", show=False),
        Binding("dollar_sign", "toggle_tokens", "Tokens", show=False),
    ]

    # Go-to shortcut mapping: character → (module_path, class_name)
    _GOTO_MAP: dict[str, tuple[str, str]] = {
        "c": ("alice_cli.tui.screens.chat", "ChatScreen"),
        "i": ("alice_cli.tui.screens.invoke", "InvokeScreen"),
        "d": ("alice_cli.tui.screens.diagnose", "DiagnoseScreen"),
        "s": ("alice_cli.tui.screens.status", "StatusScreen"),
        "r": ("alice_cli.tui.screens.recall", "RecallScreen"),
        "m": ("alice_cli.tui.screens.compare", "CompareScreen"),
        "o": ("alice_cli.tui.screens.compose", "ComposeScreen"),
    }

    def __init__(self, config: CLIConfig, *, wordstar: bool = False) -> None:
        super().__init__()
        self.alice_config = config
        self.wordstar = wordstar
        self.tui_state = load_tui_state()
        self._goto_pending: bool = False
        if wordstar:
            self.TITLE = "ALiCE · WordStar Mode"

    def on_mount(self) -> None:
        # Mount the TokenTracker widget at the top of the app
        from alice_cli.tui.widgets.token_tracker import TokenTracker

        tracker = TokenTracker()
        self.mount(tracker)
        # Restore visibility from state
        if not self.tui_state.token_dashboard_visible:
            tracker.display = False

        self.push_screen(HomeScreen())
        self._check_font()
        # Restore theme from persisted state
        if self.tui_state.theme in ("soft", "blue"):
            from pathlib import Path as _Path

            css_file = self._THEME_CSS.get(self.tui_state.theme, "theme.tcss")
            css_path = _Path(__file__).parent / css_file
            self.stylesheet.read(css_path)
            self.stylesheet.reparse()
        if self.wordstar:
            self.notify(
                "WordStar mode active — ^K commands are decorative, " "but the typewriter is real.",
                severity="information",
                timeout=5,
            )

    def _text_input_focused(self) -> bool:
        """Return True when an Input or TextArea widget currently has focus."""
        from textual.widgets import Input, TextArea

        return isinstance(self.focused, Input | TextArea)

    def key_g(self) -> None:
        """Set the go-to pending flag for the two-key g+<char> shortcut.

        No-ops when a text input is focused so ``g`` types normally.
        """
        if self._text_input_focused():
            return
        self._goto_pending = True

    def on_key(self, event: Any) -> None:
        """Handle the second key of a g+<char> go-to shortcut.

        If the pending flag is set, consume the next character and navigate
        to the matching screen.  Unknown characters silently clear the flag.
        """
        if not self._goto_pending:
            return
        self._goto_pending = False
        char = event.character
        if char is None:
            return
        entry = self._GOTO_MAP.get(char)
        if entry is not None:
            import importlib

            mod = importlib.import_module(entry[0])
            screen_cls = getattr(mod, entry[1])
            self.push_screen(screen_cls())

    def _check_font(self) -> None:
        """Show a one-time hint if the terminal font isn't one of the recommended ones."""
        font = _detect_terminal_font()
        if font is None:
            # Can't detect — don't nag.
            return
        if not any(rec.lower() in font.lower() for rec in RECOMMENDED_FONTS):
            self.notify(HINT_FONT, severity="information", timeout=6)

    async def action_pop_screen(self) -> None:
        if len(self.screen_stack) <= 1:
            await self.action_quit()
        else:
            self.pop_screen()

    async def action_quit(self) -> None:
        # Persist cumulative session tokens from the TokenTracker widget
        try:
            from alice_cli.tui.widgets.token_tracker import TokenTracker

            tracker = self.query_one(TokenTracker)
            self.tui_state.session_input_tokens = tracker.input_tokens
            self.tui_state.session_output_tokens = tracker.output_tokens
        except Exception:  # noqa: BLE001
            pass  # TokenTracker may not be mounted

        save_tui_state(self.tui_state)
        from alice_cli.tui.screens.quit import QuitScreen

        self.push_screen(QuitScreen())

    # Theme cycle order: amber → soft → blue → amber
    _THEME_CYCLE = ["amber", "soft", "blue"]
    _THEME_CSS: dict[str, str] = {
        "amber": "theme.tcss",
        "soft": "theme_soft.tcss",
        "blue": "theme_blue.tcss",
    }

    def action_toggle_theme(self) -> None:
        """Cycle between 'amber', 'soft', and 'blue' theme variants."""
        from pathlib import Path as _Path

        current = self.tui_state.theme
        try:
            idx = self._THEME_CYCLE.index(current)
        except ValueError:
            idx = 0
        next_theme = self._THEME_CYCLE[(idx + 1) % len(self._THEME_CYCLE)]
        self.tui_state.theme = next_theme
        css_path = _Path(__file__).parent / self._THEME_CSS[next_theme]

        self.stylesheet.read(css_path)
        self.stylesheet.reparse()
        self.notify(f"Theme: {self.tui_state.theme}", severity="information", timeout=2)

    def action_command_palette(self) -> None:
        """Open the fuzzy-search command palette."""
        from alice_cli.tui.widgets.command_palette import CommandPalette

        def _on_dismiss(verb: str | None) -> None:
            if verb is None:
                return
            from alice_cli.tui.screens.home import _SCREEN_REGISTRY

            entry = _SCREEN_REGISTRY.get(verb)
            if entry is not None:
                import importlib

                mod = importlib.import_module(entry[0])
                screen_cls = getattr(mod, entry[1])
                self.push_screen(screen_cls())
            else:
                self.notify(f"'{verb}' is CLI-only. Use: alice {verb}", severity="information")

        self.push_screen(CommandPalette(), callback=_on_dismiss)

    def action_help(self) -> None:
        """Open the keybinding help overlay."""
        from alice_cli.tui.widgets.help_overlay import HelpOverlay

        self.push_screen(HelpOverlay())

    def action_toggle_tokens(self) -> None:
        """Toggle the TokenTracker widget visibility."""
        try:
            from alice_cli.tui.widgets.token_tracker import TokenTracker

            tracker = self.query_one(TokenTracker)
            tracker.display = not tracker.display
            self.tui_state.token_dashboard_visible = tracker.display
        except Exception:  # noqa: BLE001
            pass  # TokenTracker may not be mounted

    def _update_breadcrumbs(self) -> None:
        """Update BreadcrumbBar on the active screen to reflect the screen stack."""
        from alice_cli.tui.widgets.breadcrumb import BreadcrumbBar

        # Build path from screen stack (skip the default screen)
        path = []
        for screen in self.screen_stack:
            name = screen.__class__.__name__.removesuffix("Screen") or "Home"
            path.append(name)

        try:
            bar = self.screen.query_one(BreadcrumbBar)
            bar.path = path
        except Exception:  # noqa: BLE001
            pass  # Screen may not have a BreadcrumbBar

    def on_screen_resume(self) -> None:
        """Called when a screen becomes active (after push or pop)."""
        self._update_breadcrumbs()
