"""DSL parser — maps natural language phrases to proxym API calls.

Simple pattern matching that works without LLM. Handles both Polish and English
phrases. Falls back to None when no pattern matches (caller should route to LLM).

Usage:
    parser = DSLParser()
    cmd = parser.parse("pokaż VM-y")
    if cmd:
        result = parser.execute(cmd)
"""

from __future__ import annotations

import re
import time
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

import yaml

import structlog

logger = structlog.get_logger()

_DEFAULT_DSL_PATH = Path.home() / ".config" / "proxym" / "dsl.yaml"

# Bundled DSL used when no user config exists
_BUNDLED_DSL: dict[str, Any] = {
    "version": "1.0",
    "queries": {
        "status": {
            "patterns": ["status", "jak idzie", "co działa", "pokaż stan", "show status"],
            "action": "GET /dashboard/system",
            "format": "table",
        },
        "vms": {
            "patterns": [
                "pokaż VM-y", "lista maszyn", "które VM-y działają",
                "vm list", "list vms", "show vms",
            ],
            "action": "GET /dashboard/vms",
            "format": "table",
        },
        "costs": {
            "patterns": [
                "ile wydałem", "koszty", "podsumowanie budżetu",
                "costs", "show costs", "budget",
            ],
            "action": "GET /dashboard/costs",
            "format": "chart",
        },
        "logs": {
            "patterns": [
                "logi", "błędy", "ostatnie zdarzenia", "co się stało",
                "logs", "show logs", "errors",
            ],
            "action": "GET /dashboard/logs?lines=50",
            "format": "text",
        },
        "models": {
            "patterns": ["jakie modele", "dostępne modele", "models", "list models"],
            "action": "GET /v1/models",
            "format": "table",
        },
        "tickets": {
            "patterns": [
                "list tickets", "show tickets", "pokaż tickety", "zadania",
                "tickets", "tickety",
            ],
            "action": "GET /dashboard/tickets",
            "format": "table",
        },
        "ticket_board": {
            "patterns": ["board", "kanban", "tablica"],
            "action": "GET /dashboard/tickets/board/view",
            "format": "table",
        },
        "sprints": {
            "patterns": ["list sprints", "sprinty", "show sprints", "sprints"],
            "action": "GET /dashboard/sprints",
            "format": "table",
        },
        "diagnose": {
            "patterns": ["diagnose", "diagnostics", "run tests", "sprawdź", "testy"],
            "action": "GET /tests/status",
            "format": "text",
        },
        "projects": {
            "patterns": ["list projects", "projekty", "show projects", "projects"],
            "action": "GET /dashboard/projects",
            "format": "table",
        },
        "providers": {
            "patterns": ["providers", "list providers", "providery", "dostawcy"],
            "action": "GET /dashboard/providers",
            "format": "table",
        },
    },
    "commands": {
        "vm_create": {
            "patterns": [
                "stwórz VM {tool} dla {project}",
                "nowa maszyna {tool} projekt {project}",
                "utwórz {tool} dla {project}",
                "create vm {tool} for {project}",
                "create {tool} for {project}",
            ],
            "action": "POST /dashboard/vms",
            "params_from_pattern": ["tool", "project"],
            "defaults": {"account": "cheapest_active"},
        },
        "vm_start": {
            "patterns": [
                "uruchom {vm_id}", "start {vm_id}", "włącz {vm_id}",
            ],
            "action": "POST /dashboard/vms/{vm_id}/start",
        },
        "vm_stop": {
            "patterns": [
                "zatrzymaj {vm_id}", "stop {vm_id}", "wyłącz {vm_id}",
            ],
            "action": "POST /dashboard/vms/{vm_id}/stop",
        },
        "vm_snapshot": {
            "patterns": [
                "snapshot {vm_id} jako {name}",
                "snapshot {vm_id} as {name}",
                "snapshot {vm_id}", "zapisz stan {vm_id}",
            ],
            "action": "POST /dashboard/vms/{vm_id}/snapshot",
            "params_from_pattern": ["vm_id", "name"],
            "defaults": {},
        },
        "switch_project": {
            "patterns": [
                "przełącz {vm_id} na {project}",
                "zmień projekt {vm_id} na {project}",
                "switch {vm_id} to {project}",
            ],
            "action": "POST /dashboard/vms/{vm_id}/switch-project",
            "params_from_pattern": ["vm_id", "project"],
        },
        "add_account": {
            "patterns": [
                "dodaj konto {provider} klucz {key}",
                "nowe konto {provider}",
                "add account {provider} key {key}",
            ],
            "action": "POST /dashboard/accounts",
        },
    },
}


@dataclass
class ParsedCommand:
    """Result of successfully parsing a user phrase against the DSL."""

    action_type: str        # "query" or "command"
    endpoint: str           # e.g. "GET /api/v1/vms"
    params: dict[str, Any] = field(default_factory=dict)
    raw_pattern: str = ""
    confidence: float = 1.0
    format: str = "text"


class DSLParser:
    """Parse natural language phrases into proxym API calls.

    Works without LLM — pure pattern matching against a YAML DSL definition.
    """

    def __init__(self, dsl_path: Path | None = None) -> None:
        self._dsl = self._load_dsl(dsl_path)
        self._patterns = self._compile_patterns()

    @staticmethod
    def _load_dsl(dsl_path: Path | None) -> dict:
        """Load DSL from file or use bundled defaults."""
        path = dsl_path or _DEFAULT_DSL_PATH
        if path.exists():
            with open(path) as f:
                return yaml.safe_load(f)
        return _BUNDLED_DSL

    def _compile_patterns(self) -> list[tuple]:
        """Compile all DSL patterns into (regex, action_type, name, spec) tuples."""
        section_type = {"queries": "query", "commands": "command"}
        compiled: list[tuple] = []
        for section in ("queries", "commands"):
            for name, spec in self._dsl.get(section, {}).items():
                for pattern in spec.get("patterns", []):
                    # Convert {placeholder} to named capture groups
                    regex_str = re.sub(r"\{(\w+)\}", r"(?P<\1>[\\w._-]+)", pattern)
                    compiled.append((
                        re.compile(regex_str, re.IGNORECASE),
                        section_type[section],
                        name,
                        spec,
                    ))
        return compiled

    def parse(self, text: str) -> ParsedCommand | None:
        """Try to match *text* against DSL patterns.

        Returns ``None`` if no pattern matches — caller should forward to LLM.
        """
        text = text.strip()

        for regex, action_type, name, spec in self._patterns:
            m = regex.fullmatch(text) or regex.match(text)
            if m:
                return self._build_command(m.groupdict(), action_type, name, spec)

        return None

    @staticmethod
    def _resolve_default(value: Any) -> Any:
        """Resolve dynamic default values like ``auto-{timestamp}``."""
        if isinstance(value, str) and value == "auto-{timestamp}":
            return f"auto-{int(time.time())}"
        return value

    def _build_command(
        self, params: dict, action_type: str, name: str, spec: dict,
    ) -> ParsedCommand:
        """Build a ParsedCommand from matched groups and spec."""
        for k, v in spec.get("defaults", {}).items():
            if k not in params:
                params[k] = self._resolve_default(v)

        endpoint = spec["action"]
        for k, v in params.items():
            endpoint = endpoint.replace(f"{{{k}}}", str(v))

        logger.debug("dsl_match", pattern=name, params=params, endpoint=endpoint)
        return ParsedCommand(
            action_type=action_type,
            endpoint=endpoint,
            params=params,
            raw_pattern=name,
            confidence=1.0,
            format=spec.get("format", "text"),
        )

    def execute(self, cmd: ParsedCommand) -> dict:
        """Execute a parsed command against the proxym API."""
        import httpx

        from proxym.config import get_settings

        settings = get_settings()

        parts = cmd.endpoint.split(" ", 1)
        if len(parts) != 2:
            return {"error": f"Invalid endpoint format: {cmd.endpoint}"}
        method, path = parts
        url = f"http://localhost:{settings.port}{path}"
        headers = {"Authorization": f"Bearer {settings.master_key}"}

        try:
            with httpx.Client(timeout=10, headers=headers) as client:
                if method.upper() == "GET":
                    r = client.get(url)
                else:
                    r = client.post(url, json=cmd.params)
                return r.json()
        except httpx.ConnectError:
            return {"error": "Cannot connect to proxym server"}
        except Exception as exc:
            return {"error": str(exc)}

    def list_patterns(self) -> list[dict[str, Any]]:
        """Return all registered patterns for help / autocomplete."""
        section_type = {"queries": "query", "commands": "command"}
        results = []
        for section in ("queries", "commands"):
            for name, spec in self._dsl.get(section, {}).items():
                results.append({
                    "name": name,
                    "type": section_type[section],
                    "patterns": spec.get("patterns", []),
                    "action": spec.get("action", ""),
                })
        return results
