from __future__ import annotations

import json
import re
import shutil
import subprocess
from typing import Any, Dict, Sequence


def tailscale_cli_available() -> bool:
    return shutil.which("tailscale") is not None


def ssh_cli_available() -> bool:
    return shutil.which("ssh") is not None


def _normalize_host(host: str | None) -> str:
    value = str(host or "").strip()
    if value in {"", "localhost", "127.0.0.1", "::1"}:
        return ""
    return value


def _run_command(args: Sequence[str], *, host: str | None = None, timeout: int = 15) -> subprocess.CompletedProcess[str]:
    target = _normalize_host(host)
    command = list(args)
    if target:
        command = [
            "ssh",
            "-o",
            "BatchMode=yes",
            "-o",
            "ConnectTimeout=10",
            target,
            *command,
        ]
    return subprocess.run(
        command,
        capture_output=True,
        text=True,
        timeout=timeout,
        check=False,
    )


def tailscale_status_summary(host: str | None = None) -> Dict[str, Any]:
    target = _normalize_host(host)
    summary: Dict[str, Any] = {
        "available": False,
        "logged_in": False,
        "magicdns_name": "",
        "tailnet_name": "",
        "reachable": False,
        "last_check_error": "",
        "host": target,
    }
    if target:
        if not ssh_cli_available():
            summary["last_check_error"] = "SSH is not installed on this host."
            return summary
    elif not tailscale_cli_available():
        summary["last_check_error"] = "Tailscale CLI is not installed on this host."
        return summary

    summary["available"] = True
    try:
        result = _run_command(["tailscale", "status", "--json"], host=target, timeout=15)
    except Exception as exc:
        summary["last_check_error"] = f"Armorer could not query Tailscale. {exc}"
        return summary

    if result.returncode != 0:
        detail = (result.stderr or result.stdout or "").strip()
        if "command not found" in detail.lower():
            summary["available"] = False
        summary["last_check_error"] = detail or "Tailscale status command failed."
        return summary

    try:
        payload = json.loads(result.stdout or "{}")
    except Exception:
        summary["last_check_error"] = "Tailscale returned invalid JSON."
        return summary

    self_info = payload.get("Self") if isinstance(payload, dict) else {}
    if not isinstance(self_info, dict):
        self_info = {}
    current_tailnet = payload.get("CurrentTailnet") if isinstance(payload, dict) else {}
    if not isinstance(current_tailnet, dict):
        current_tailnet = {}

    backend_state = str(payload.get("BackendState") or "").strip()
    auth_url = str(payload.get("AuthURL") or "").strip()
    magicdns_name = str(self_info.get("DNSName") or "").strip().rstrip(".")
    tailnet_name = str(current_tailnet.get("MagicDNSSuffix") or current_tailnet.get("Name") or "").strip()

    logged_in = bool(backend_state.lower() == "running" and self_info and not auth_url)
    reachable = bool(logged_in and magicdns_name)

    summary.update(
        {
            "logged_in": logged_in,
            "magicdns_name": magicdns_name,
            "tailnet_name": tailnet_name,
            "reachable": reachable,
            "last_check_error": "" if reachable or logged_in else "Sign in to Tailscale on this host to enable remote OpenClaw access.",
        }
    )
    return summary


def tailscale_serve_status_summary(host: str | None = None) -> Dict[str, Any]:
    target = _normalize_host(host)
    summary: Dict[str, Any] = {
        "available": False,
        "enabled": False,
        "configured": False,
        "enable_url": "",
        "last_check_error": "",
        "host": target,
    }
    if target:
        if not ssh_cli_available():
            summary["last_check_error"] = "SSH is not installed on this host."
            return summary
    elif not tailscale_cli_available():
        summary["last_check_error"] = "Tailscale CLI is not installed on this host."
        return summary

    summary["available"] = True
    try:
        result = _run_command(["tailscale", "serve", "status", "--json"], host=target, timeout=15)
    except Exception as exc:
        summary["last_check_error"] = f"Armorer could not query Tailscale Serve. {exc}"
        return summary

    combined_output = "\n".join(
        part.strip() for part in [result.stdout or "", result.stderr or ""] if part.strip()
    ).strip()
    enable_url_match = re.search(r"https://\S+", combined_output)
    enable_url = enable_url_match.group(0) if enable_url_match else ""

    if result.returncode == 0:
        configured = False
        try:
            payload = json.loads(result.stdout or "{}")
            configured = bool(payload.get("Foreground") or payload.get("Background"))
        except Exception:
            configured = "no serve config" not in combined_output.lower()
        summary.update(
            {
                "enabled": True,
                "configured": configured,
                "enable_url": "",
                "last_check_error": "",
            }
        )
        return summary

    detail = combined_output or "Tailscale Serve status command failed."
    if "serve is not enabled on your tailnet" in detail.lower():
        summary.update(
            {
                "enabled": False,
                "configured": False,
                "enable_url": enable_url,
                "last_check_error": "Enable Tailscale Serve in your tailnet before using remote OpenClaw access.",
            }
        )
        return summary

    if "command not found" in detail.lower():
        summary["available"] = False
    summary["enable_url"] = enable_url
    summary["last_check_error"] = detail
    return summary


def ensure_tailscale_serve(target: str, *, host: str | None = None) -> Dict[str, Any]:
    normalized_target = str(target or "").strip()
    if not normalized_target:
        return {
            "ok": False,
            "configured": False,
            "url": "",
            "magicdns_name": "",
            "message": "No Tailscale serve target was provided.",
        }

    status = tailscale_status_summary(host)
    if not status.get("available"):
        return {
            "ok": False,
            "configured": False,
            "url": "",
            "magicdns_name": "",
            "message": str(status.get("last_check_error") or "Tailscale is not available."),
        }
    if not status.get("logged_in"):
        return {
            "ok": False,
            "configured": False,
            "url": "",
            "magicdns_name": "",
            "message": str(status.get("last_check_error") or "Sign in to Tailscale before enabling remote access."),
        }
    serve_status = tailscale_serve_status_summary(host)
    if serve_status.get("available") and not serve_status.get("enabled"):
        return {
            "ok": False,
            "configured": False,
            "url": "",
            "magicdns_name": str(status.get("magicdns_name") or "").strip(),
            "message": str(serve_status.get("last_check_error") or "Enable Tailscale Serve before enabling remote access."),
            "enable_url": str(serve_status.get("enable_url") or "").strip(),
        }
    magicdns_name = str(status.get("magicdns_name") or "").strip()
    if not magicdns_name:
        return {
            "ok": False,
            "configured": False,
            "url": "",
            "magicdns_name": "",
            "message": "Tailscale did not report a MagicDNS name for this host.",
        }

    try:
        result = _run_command(["tailscale", "serve", normalized_target], host=_normalize_host(host), timeout=20)
    except Exception as exc:
        return {
            "ok": False,
            "configured": False,
            "url": "",
            "magicdns_name": magicdns_name,
            "message": f"Armorer could not configure Tailscale Serve. {exc}",
        }

    if result.returncode != 0:
        detail = (result.stderr or result.stdout or "").strip()
        return {
            "ok": False,
            "configured": False,
            "url": "",
            "magicdns_name": magicdns_name,
            "message": detail or "Tailscale Serve configuration failed.",
            "enable_url": str(tailscale_serve_status_summary(host).get("enable_url") or "").strip(),
        }

    return {
        "ok": True,
        "configured": True,
        "url": f"https://{magicdns_name}",
        "magicdns_name": magicdns_name,
        "message": "",
    }
