"""Backend configuration modal — transport-specific options for installing backends.

Supports configuring stdio, sse, and streamable-http backends with
all relevant options. Used both for registry installs (pre-filled)
and custom backend add (empty form).
"""

from __future__ import annotations

import logging
from typing import Any, Dict, Optional, Tuple

from textual.app import ComposeResult
from textual.containers import Horizontal, Vertical, VerticalScroll
from textual.screen import ModalScreen
from textual.widgets import Button, Input, Label, Select, Static, Switch

from argus_mcp.registry.models import ServerEntry

logger = logging.getLogger(__name__)

# Transport types
_TRANSPORTS = [
    ("stdio", "stdio — Local subprocess"),
    ("sse", "sse — Server-Sent Events"),
    ("streamable-http", "streamable-http — HTTP streaming"),
]


class BackendConfigModal(ModalScreen[Optional[Tuple[str, Dict[str, Any]]]]):
    """Modal for configuring a backend before installation.

    Returns ``(name, config_dict)`` on install, or ``None`` on cancel.

    Parameters
    ----------
    entry : ServerEntry | None
        If provided, pre-fills fields from the registry entry.
    """

    DEFAULT_CSS = """
    BackendConfigModal {
        align: center middle;
    }
    #bcm-dialog {
        width: 80;
        max-width: 90%;
        height: auto;
        max-height: 90%;
        border: thick $primary;
        background: $surface;
        padding: 1 2;
    }
    #bcm-title {
        text-style: bold;
        color: $primary;
        margin-bottom: 1;
    }
    #bcm-scroll {
        height: auto;
        max-height: 50;
    }
    .bcm-section {
        margin-top: 1;
        margin-bottom: 0;
        text-style: bold;
        color: $accent;
    }
    .bcm-field-label {
        margin-top: 1;
        color: $text;
    }
    .bcm-field-hint {
        color: $text-muted;
        text-style: italic;
    }
    #bcm-type-fields {
        height: auto;
    }
    #bcm-actions {
        height: 3;
        align: right middle;
        margin-top: 1;
    }
    #bcm-actions Button {
        margin-left: 1;
    }
    #bcm-preview {
        margin-top: 1;
        color: $text-muted;
        height: auto;
        max-height: 8;
    }
    """

    BINDINGS = [
        ("escape", "cancel", "Close"),
        ("ctrl+s", "install", "Install"),
    ]

    def __init__(self, entry: Optional[ServerEntry] = None, **kwargs: Any) -> None:
        super().__init__(**kwargs)
        self._entry = entry
        self._mode = "registry" if entry else "custom"

    def compose(self) -> ComposeResult:
        entry = self._entry
        title = f"Configure: {entry.name}" if entry else "Add New Backend"

        with Vertical(id="bcm-dialog"):
            yield Label(f"[b]{title}[/b]", id="bcm-title")

            with VerticalScroll(id="bcm-scroll"):
                # Common fields
                yield Label("General", classes="bcm-section")

                yield Label("Backend Name", classes="bcm-field-label")
                yield Input(
                    value=entry.name if entry else "",
                    placeholder="e.g., my-mcp-server",
                    id="bcm-name",
                )

                yield Label("Transport Type", classes="bcm-field-label")
                transport_val = entry.transport if entry else Select.BLANK
                yield Select(
                    [(label, val) for val, label in _TRANSPORTS],
                    value=transport_val,
                    id="bcm-transport",
                    allow_blank=not bool(entry),
                )

                yield Label("Group", classes="bcm-field-label")
                yield Label("Assign to a server group (optional)", classes="bcm-field-hint")
                yield Input(
                    value="",
                    placeholder="default",
                    id="bcm-group",
                )

                yield Label("Enabled", classes="bcm-field-label")
                yield Switch(value=True, id="bcm-enabled")

                # Type-specific fields
                yield Label("Transport Options", classes="bcm-section")
                with Vertical(id="bcm-type-fields"):
                    # stdio fields
                    yield Label("Command", classes="bcm-field-label", id="lbl-command")
                    yield Input(
                        value=entry.command if entry and entry.command else "",
                        placeholder="e.g., npx -y @org/mcp-server",
                        id="bcm-command",
                    )

                    yield Label(
                        "Arguments (space-separated)",
                        classes="bcm-field-label",
                        id="lbl-args",
                    )
                    args_str = " ".join(entry.args) if entry and entry.args else ""
                    yield Input(
                        value=args_str,
                        placeholder="e.g., --port 8080 --verbose",
                        id="bcm-args",
                    )

                    yield Label(
                        "Environment Variables (KEY=VALUE, one per line)",
                        classes="bcm-field-label",
                        id="lbl-env",
                    )
                    yield Label(
                        "e.g., API_KEY=secret123",
                        classes="bcm-field-hint",
                        id="hint-env",
                    )
                    yield Input(
                        value="",
                        placeholder="KEY=VALUE (comma-separated for multiple)",
                        id="bcm-env",
                    )

                    # URL fields (sse / streamable-http)
                    yield Label("URL", classes="bcm-field-label", id="lbl-url")
                    yield Input(
                        value=entry.url if entry and entry.url else "",
                        placeholder="e.g., https://mcp.example.com/sse",
                        id="bcm-url",
                    )

                    yield Label(
                        "Headers (KEY:VALUE, comma-separated)",
                        classes="bcm-field-label",
                        id="lbl-headers",
                    )
                    yield Input(
                        value="",
                        placeholder="Authorization:Bearer xxx, X-Custom:val",
                        id="bcm-headers",
                    )

                # Advanced
                yield Label("Advanced", classes="bcm-section")

                yield Label("Timeout (seconds)", classes="bcm-field-label")
                yield Input(
                    value="",
                    placeholder="30",
                    id="bcm-timeout",
                    type="number",
                )

                yield Label(
                    "Filters (tool name patterns, comma-separated)", classes="bcm-field-label"
                )
                yield Label(
                    "Include/exclude tools: +pattern or -pattern",
                    classes="bcm-field-hint",
                )
                yield Input(
                    value="",
                    placeholder="+useful_tool, -debug_*",
                    id="bcm-filters",
                )

                # Preview
                yield Static("", id="bcm-preview")

            # Actions
            with Horizontal(id="bcm-actions"):
                yield Button(
                    "Install" if self._mode == "registry" else "Add Backend",
                    variant="success",
                    id="btn-bcm-install",
                )
                yield Button("Cancel", variant="default", id="btn-bcm-cancel")

    def on_mount(self) -> None:
        """Set initial field visibility based on transport type."""
        self._update_type_fields()

    def on_select_changed(self, event: Select.Changed) -> None:
        """Update visible fields when transport type changes."""
        if event.select.id == "bcm-transport":
            self._update_type_fields()
            self._update_preview()

    def on_input_changed(self, event: Input.Changed) -> None:
        """Update config preview when any field changes."""
        self._update_preview()

    def _update_type_fields(self) -> None:
        """Show/hide fields based on the selected transport type."""
        transport_sel = self.query_one("#bcm-transport", Select)
        transport = transport_sel.value if transport_sel.value != Select.BLANK else None

        is_stdio = transport == "stdio"
        is_url = transport in ("sse", "streamable-http")

        # stdio fields
        for widget_id in (
            "lbl-command",
            "bcm-command",
            "lbl-args",
            "bcm-args",
            "lbl-env",
            "hint-env",
            "bcm-env",
        ):
            try:
                w = self.query_one(f"#{widget_id}")
                w.display = is_stdio
            except Exception:  # noqa: BLE001
                pass

        # URL fields
        for widget_id in ("lbl-url", "bcm-url", "lbl-headers", "bcm-headers"):
            try:
                w = self.query_one(f"#{widget_id}")
                w.display = is_url
            except Exception:  # noqa: BLE001
                pass

    def _update_preview(self) -> None:
        """Update the config preview text."""
        config = self._build_config()
        name = self._get_name()
        if config and name:
            import json

            preview_text = (
                f"[dim]Config preview for '{name}':[/dim]\n{json.dumps(config, indent=2)}"
            )
        else:
            preview_text = "[dim]Fill in required fields to see config preview[/dim]"

        try:
            self.query_one("#bcm-preview", Static).update(preview_text)
        except Exception:  # noqa: BLE001
            pass

    def _get_name(self) -> str:
        """Get the backend name from the input."""
        try:
            return self.query_one("#bcm-name", Input).value.strip()
        except Exception:  # noqa: BLE001
            return ""

    def _build_config(self) -> Optional[Dict[str, Any]]:
        """Build the backend config dict from current form values."""
        try:
            transport_sel = self.query_one("#bcm-transport", Select)
            transport = transport_sel.value
            if transport == Select.BLANK:
                return None
        except Exception:  # noqa: BLE001
            return None

        config: Dict[str, Any] = {"type": transport}

        if transport == "stdio":
            command = self.query_one("#bcm-command", Input).value.strip()
            if not command:
                return None
            config["command"] = command

            args = self.query_one("#bcm-args", Input).value.strip()
            if args:
                config["args"] = args.split()

            env_str = self.query_one("#bcm-env", Input).value.strip()
            if env_str:
                env = {}
                for pair in env_str.split(","):
                    pair = pair.strip()
                    if "=" in pair:
                        k, v = pair.split("=", 1)
                        env[k.strip()] = v.strip()
                if env:
                    config["env"] = env

        elif transport in ("sse", "streamable-http"):
            url = self.query_one("#bcm-url", Input).value.strip()
            if not url:
                return None
            config["url"] = url

            headers_str = self.query_one("#bcm-headers", Input).value.strip()
            if headers_str:
                headers = {}
                for pair in headers_str.split(","):
                    pair = pair.strip()
                    if ":" in pair:
                        k, v = pair.split(":", 1)
                        headers[k.strip()] = v.strip()
                if headers:
                    config["headers"] = headers

        # Common optional fields
        timeout_str = self.query_one("#bcm-timeout", Input).value.strip()
        if timeout_str:
            try:
                config["timeout"] = int(timeout_str)
            except ValueError:
                pass

        filters_str = self.query_one("#bcm-filters", Input).value.strip()
        if filters_str:
            include = []
            exclude = []
            for pat in filters_str.split(","):
                pat = pat.strip()
                if pat.startswith("-"):
                    exclude.append(pat[1:])
                elif pat.startswith("+"):
                    include.append(pat[1:])
                else:
                    include.append(pat)
            if include or exclude:
                f: Dict[str, Any] = {}
                if include:
                    f["include"] = include
                if exclude:
                    f["exclude"] = exclude
                config["filters"] = f

        enabled = self.query_one("#bcm-enabled", Switch).value
        if not enabled:
            config["enabled"] = False

        group = self.query_one("#bcm-group", Input).value.strip()
        if group:
            config["group"] = group

        return config

    def _validate(self) -> Optional[str]:
        """Validate form and return error message or None if valid."""
        name = self._get_name()
        if not name:
            return "Backend name is required"
        if " " in name:
            return "Backend name must not contain spaces"

        transport_sel = self.query_one("#bcm-transport", Select)
        if transport_sel.value == Select.BLANK:
            return "Transport type is required"

        transport = transport_sel.value

        if transport == "stdio":
            command = self.query_one("#bcm-command", Input).value.strip()
            if not command:
                return "Command is required for stdio backends"
        elif transport in ("sse", "streamable-http"):
            url = self.query_one("#bcm-url", Input).value.strip()
            if not url:
                return "URL is required for remote backends"

        return None

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "btn-bcm-install":
            self.action_install()
        elif event.button.id == "btn-bcm-cancel":
            self.action_cancel()

    def action_install(self) -> None:
        """Validate and dismiss with (name, config) tuple."""
        error = self._validate()
        if error:
            self.app.notify(error, severity="error")
            return

        name = self._get_name()
        config = self._build_config()
        if config is None:
            self.app.notify("Cannot build config — fill required fields", severity="error")
            return

        self.dismiss((name, config))

    def action_cancel(self) -> None:
        """Dismiss without installing."""
        self.dismiss(None)
