# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Proton Services Skill

Health monitoring for Proton Mail Bridge and control of Proton VPN
via the protonvpn-cli. No pip dependencies — uses only socket and subprocess.
"""

import logging
import os
import shutil
import socket
import subprocess

logger = logging.getLogger(__name__)

# Default Proton Mail Bridge ports
BRIDGE_IMAP_HOST = "127.0.0.1"
BRIDGE_IMAP_PORT = 1143
BRIDGE_SMTP_PORT = 1025


def _check_port(host: str, port: int, timeout: float = 2.0) -> bool:
    """Return True if a TCP connection to host:port succeeds."""
    try:
        with socket.create_connection((host, port), timeout=timeout):
            return True
    except (OSError, ConnectionRefusedError, TimeoutError):
        return False


def _vpn_cli_available() -> bool:
    """Return True if protonvpn-cli is on PATH."""
    return shutil.which("protonvpn-cli") is not None


def _run_vpn_cmd(*args: str, timeout: int = 15) -> tuple[int, str]:
    """Run a protonvpn-cli command and return (returncode, stdout+stderr)."""
    try:
        result = subprocess.run(
            ["protonvpn-cli", *args],
            capture_output=True,
            text=True,
            timeout=timeout,
        )
        output = (result.stdout + result.stderr).strip()
        return result.returncode, output
    except FileNotFoundError:
        return -1, "protonvpn-cli not found on PATH"
    except subprocess.TimeoutExpired:
        return -1, "protonvpn-cli command timed out"


def proton_status(data: dict) -> str:
    """Check Proton Mail Bridge and VPN status."""
    lines = ["🔒 Proton Services Status", ""]

    # Bridge
    host = os.environ.get("PROTON_BRIDGE_HOST", BRIDGE_IMAP_HOST)
    imap_port = int(os.environ.get("PROTON_BRIDGE_IMAP_PORT", str(BRIDGE_IMAP_PORT)))
    smtp_port = int(os.environ.get("PROTON_BRIDGE_SMTP_PORT", str(BRIDGE_SMTP_PORT)))

    imap_up = _check_port(host, imap_port)
    smtp_up = _check_port(host, smtp_port)

    if imap_up and smtp_up:
        lines.append(f"✅ Proton Mail Bridge: running ({host})")
        lines.append(f"   IMAP: {host}:{imap_port}  SMTP: {host}:{smtp_port}")
    elif imap_up or smtp_up:
        lines.append(f"⚠️ Proton Mail Bridge: partially running ({host})")
        lines.append(
            f"   IMAP {imap_port}: {'up' if imap_up else 'DOWN'}  "
            f"SMTP {smtp_port}: {'up' if smtp_up else 'DOWN'}"
        )
    else:
        lines.append("❌ Proton Mail Bridge: not running")
        lines.append(f"   Expected on {host}:{imap_port} (IMAP) / {host}:{smtp_port} (SMTP)")
        lines.append(
            "   Start Bridge from your applications menu or run: protonmail-bridge --noninteractive"
        )

    # Email config health
    email_addr = os.environ.get("EMAIL_ADDRESS", "")
    email_imap = os.environ.get("EMAIL_IMAP_SERVER", "")
    email_imap_port = os.environ.get("EMAIL_IMAP_PORT", "993")
    if email_addr and email_addr.split("@")[-1].lower() in ("proton.me", "protonmail.com", "pm.me"):
        lines.append("")
        if email_imap in ("127.0.0.1", "localhost") and email_imap_port == str(imap_port):
            lines.append("✅ Email config: pointing at Bridge")
        elif email_imap in ("127.0.0.1", "localhost"):
            lines.append(
                f"⚠️ Email config: IMAP port is {email_imap_port}, Bridge expects {imap_port}"
            )
        else:
            lines.append(
                f"⚠️ Email config: IMAP server is {email_imap}, should be 127.0.0.1 for Bridge"
            )

    # VPN
    lines.append("")
    if _vpn_cli_available():
        rc, output = _run_vpn_cmd("s")
        if rc == 0 and output:
            lines.append("✅ Proton VPN CLI: installed")
            for line in output.splitlines()[:5]:
                lines.append(f"   {line}")
        else:
            lines.append("✅ Proton VPN CLI: installed")
            lines.append("   Status: disconnected or not logged in")
    else:
        lines.append("⬜ Proton VPN CLI: not installed")
        lines.append("   Install: https://protonvpn.com/support/linux-vpn-setup/")

    return "\n".join(lines)


def proton_vpn_connect(data: dict) -> str:
    """Connect to Proton VPN."""
    if not _vpn_cli_available():
        return (
            "❌ protonvpn-cli is not installed.\n"
            "Install: https://protonvpn.com/support/linux-vpn-setup/"
        )

    if not data.get("_confirmed"):
        country = data.get("country", "")
        target = f"country {country.upper()}" if country else "fastest server"
        from familiar.core.confirmations import needs_confirmation

        return needs_confirmation(
            "proton_vpn_connect",
            data,
            f"Connect to Proton VPN ({target})",
            risk="medium",
        )

    country = data.get("country", "")
    if country:
        rc, output = _run_vpn_cmd("c", "--cc", country.upper(), timeout=30)
    else:
        rc, output = _run_vpn_cmd("c", "-f", timeout=30)

    if rc == 0:
        return f"✅ Proton VPN connected.\n{output}"
    return f"❌ VPN connection failed (exit {rc}).\n{output}"


def proton_vpn_disconnect(data: dict) -> str:
    """Disconnect from Proton VPN."""
    if not _vpn_cli_available():
        return "❌ protonvpn-cli is not installed."

    if not data.get("_confirmed"):
        from familiar.core.confirmations import needs_confirmation

        return needs_confirmation(
            "proton_vpn_disconnect",
            data,
            "Disconnect from Proton VPN",
            risk="medium",
        )

    rc, output = _run_vpn_cmd("d", timeout=15)
    if rc == 0:
        return f"✅ Proton VPN disconnected.\n{output}"
    return f"❌ VPN disconnect failed (exit {rc}).\n{output}"


def proton_vpn_status(data: dict) -> str:
    """Get detailed Proton VPN status."""
    if not _vpn_cli_available():
        return (
            "❌ protonvpn-cli is not installed.\n"
            "Install: https://protonvpn.com/support/linux-vpn-setup/"
        )

    rc, output = _run_vpn_cmd("s")
    if rc == 0 and output:
        return f"🔒 Proton VPN Status:\n\n{output}"
    return "⬜ Proton VPN: disconnected or not logged in."


TOOLS = [
    {
        "name": "proton_status",
        "description": (
            "Check Proton services health including Mail Bridge IMAP/SMTP port connectivity, "
            "email config alignment, and VPN connection state. "
            "Use this when the user asks about Proton service status or troubleshooting email delivery. "
            "Reads Bridge host/port from environment variables or defaults to 127.0.0.1:1143/1025."
        ),
        "input_schema": {"type": "object", "properties": {}},
        "handler": proton_status,
        "category": "proton",
    },
    {
        "name": "proton_vpn_connect",
        "description": (
            "Connect to Proton VPN via protonvpn-cli, optionally targeting a specific country. "
            "Defaults to the fastest available server if no country code is provided. "
            "Requires user confirmation before executing. "
            "Returns an error if protonvpn-cli is not installed."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "country": {
                    "type": "string",
                    "description": "Two-letter country code (e.g. US, CH, NL). Omit for fastest.",
                },
            },
        },
        "handler": proton_vpn_connect,
        "category": "proton",
    },
    {
        "name": "proton_vpn_disconnect",
        "description": (
            "Disconnect from Proton VPN by running protonvpn-cli disconnect. "
            "Use this when the user wants to drop the VPN tunnel. "
            "Requires user confirmation before executing."
        ),
        "input_schema": {"type": "object", "properties": {}},
        "handler": proton_vpn_disconnect,
        "category": "proton",
    },
    {
        "name": "proton_vpn_status",
        "description": (
            "Get detailed Proton VPN connection information including server, protocol, and IP address. "
            "Use this when the user wants to know which VPN server they are connected to. "
            "Returns disconnected status if the VPN is not active or protonvpn-cli is not installed."
        ),
        "input_schema": {"type": "object", "properties": {}},
        "handler": proton_vpn_status,
        "category": "proton",
    },
]
