from __future__ import annotations

import webbrowser

from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.widgets import Header, Footer

from django_shreck.messages import ModelCursorChanged, ShowDetail, ShowTable, ShowCompare, ShowCreate
from django_shreck.services.model_introspection import ModelIntrospectionService
from django_shreck.services.admin_url_resolver import AdminUrlResolver
from django_shreck.widgets.shell_widget import ShellWidget
from django_shreck.widgets.result_panel import ResultPanel
from django_shreck.widgets.model_browser_widget import ModelBrowserWidget


class ShreckApp(App):
    """Shreck — A rich TUI replacement for Django's shell_plus."""

    TITLE = "🟢 Shreck — Django TUI Shell"
    CSS_PATH = "styles/app.tcss"

    BINDINGS = [
        Binding("q", "quit", "Quit", priority=True),
        Binding("ctrl+c", "noop", "Copy", show=False, priority=False),
        Binding("a", "open_admin", "Open Admin", priority=True),
        Binding("h", "show_home", "Home", priority=True),
    ]

    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)
        self._model_namespace: dict = {}
        self._last_result: object = None

    def compose(self) -> ComposeResult:
        yield Header()
        yield ResultPanel(id="result-panel")
        yield ShellWidget(id="shell-widget")
        yield Footer()

    def on_mount(self) -> None:
        """Auto-import all Django models into the shell namespace."""
        self._model_namespace = ModelIntrospectionService.get_all_models()

        # Also add some useful imports
        from django.db.models import Q, F, Count, Sum, Avg, Max, Min

        self._model_namespace.update({
            "Q": Q,
            "F": F,
            "Count": Count,
            "Sum": Sum,
            "Avg": Avg,
            "Max": Max,
            "Min": Min,
        })

        shell = self.query_one("#shell-widget", ShellWidget)
        shell.set_namespace(self._model_namespace)

    # ── Message handlers ──────────────────────────────────────────

    def on_model_cursor_changed(self, message: ModelCursorChanged) -> None:
        """Sync shell input with the model under the browser cursor."""
        shell = self.query_one("#shell-widget", ShellWidget)
        input_widget = shell.query_one("#shell-input")
        current = input_widget.value
        # Only overwrite if the input is still a plain name prefix, not a full expression
        if not current or (current.isidentifier() and current[0].isupper()):
            expr = f"{message.model.__name__}.objects.all()"
            input_widget.value = expr
            input_widget.cursor_position = len(expr)

    def on_input_changed(self, event) -> None:
        """When the shell input changes, filter the model browser if the text is a bare prefix."""
        if event.input.id != "shell-input":
            return
        text = event.value
        # Only filter when the text is a plain CamelCase-ish prefix (no dots/parens/spaces)
        is_plain_prefix = text == "" or (text.isidentifier() and text[0].isupper())
        try:
            browser = self.query_one("#model-browser", ModelBrowserWidget)
        except Exception:
            return
        if is_plain_prefix:
            # suppress_emit=True so the browser doesn't fire ModelCursorChanged back
            # and overwrite the input the user is currently editing
            browser.set_filter(text, suppress_emit=True)

    def on_show_detail(self, message: ShowDetail) -> None:
        """Route ShowDetail message to the result panel."""
        self._last_result = message.instance
        panel = self.query_one("#result-panel", ResultPanel)
        panel.show_detail(instance=message.instance, editable=message.editable, start_editing=message.start_editing)

    def on_show_table(self, message: ShowTable) -> None:
        """Route ShowTable message to the result panel."""
        self._last_result = message.queryset
        panel = self.query_one("#result-panel", ResultPanel)
        panel.show_table(queryset=message.queryset)

    def on_show_compare(self, message: ShowCompare) -> None:
        """Route ShowCompare message to the result panel."""
        self._last_result = message.instances
        panel = self.query_one("#result-panel", ResultPanel)
        panel.show_compare(instances=message.instances)

    def on_show_create(self, message: ShowCreate) -> None:
        """Route ShowCreate message to the result panel."""
        panel = self.query_one("#result-panel", ResultPanel)
        panel.show_create(model=message.model)

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

    def action_open_admin(self) -> None:
        """Open the Django admin for the current result."""
        if self._last_result is None:
            self.notify("No result to open in admin", severity="warning")
            return

        url = AdminUrlResolver.get_admin_url_for_target(self._last_result)
        if url:
            # Try to build the full URL
            full_url = f"http://localhost:8000{url}"
            self.notify(f"Admin URL: {full_url}")
            try:
                webbrowser.open(full_url)
            except Exception:
                self.notify(f"Could not open browser. URL: {full_url}", severity="warning")
        else:
            self.notify("No admin URL available for this result", severity="warning")

    def action_noop(self) -> None:
        """No-op action used to shadow Textual's default ctrl+c quit."""

    def action_show_home(self) -> None:
        """Navigate back to the model browser (home view)."""
        panel = self.query_one("#result-panel", ResultPanel)
        panel.show_model_browser()




