"""
Cross-platform trusted CA certificate installer.

Supports: Windows, macOS, Linux (Debian/Ubuntu, RHEL/Fedora/CentOS, Arch).
Also attempts to install into Firefox's NSS certificate store when found.

Usage:
    from cert_installer import install_ca, is_ca_trusted
    install_ca("/path/to/ca.crt", cert_name="MasterHttpRelayVPN")
"""

import glob
import logging
import os
import platform
import shutil
import subprocess
import sys
import tempfile

log = logging.getLogger("Cert")

CERT_NAME = "MasterHttpRelayVPN"


# ─────────────────────────────────────────────────────────────────────────────
# Helpers
# ─────────────────────────────────────────────────────────────────────────────

def _run(cmd: list[str], *, check: bool = True, capture: bool = True) -> subprocess.CompletedProcess:
    return subprocess.run(
        cmd,
        check=check,
        stdout=subprocess.PIPE if capture else None,
        stderr=subprocess.PIPE if capture else None,
    )


def _has_cmd(name: str) -> bool:
    return shutil.which(name) is not None


# ─────────────────────────────────────────────────────────────────────────────
# Windows
# ─────────────────────────────────────────────────────────────────────────────

def _install_windows(cert_path: str, cert_name: str) -> bool:
    """
    Install into the current user's Trusted Root store (no admin required).
    Falls back to the system store if certutil fails.
    """
    # Per-user store — works without elevation
    try:
        _run(["certutil", "-addstore", "-user", "Root", cert_path])
        log.info("Certificate installed in Windows user Trusted Root store.")
        return True
    except (subprocess.CalledProcessError, FileNotFoundError) as exc:
        log.warning("certutil user store failed: %s", exc)

    # Try system store (requires admin)
    try:
        _run(["certutil", "-addstore", "Root", cert_path])
        log.info("Certificate installed in Windows system Trusted Root store.")
        return True
    except (subprocess.CalledProcessError, FileNotFoundError) as exc:
        log.error("certutil system store failed: %s", exc)

    # Fallback: use PowerShell
    try:
        ps_cmd = (
            f"Import-Certificate -FilePath '{cert_path}' "
            f"-CertStoreLocation Cert:\\CurrentUser\\Root"
        )
        _run(["powershell", "-NoProfile", "-Command", ps_cmd])
        log.info("Certificate installed via PowerShell.")
        return True
    except (subprocess.CalledProcessError, FileNotFoundError) as exc:
        log.error("PowerShell install failed: %s", exc)

    return False


def _is_trusted_windows(cert_path: str) -> bool:
    # Check by thumbprint only — the cert name is also the folder name, so
    # a plain string search on certutil output would produce a false positive.
    thumbprint = _cert_thumbprint(cert_path)
    if not thumbprint:
        return False
    try:
        result = _run(["certutil", "-user", "-store", "Root"])
        output = result.stdout.decode(errors="replace").upper()
        return thumbprint in output
    except Exception:
        return False


def _cert_thumbprint(cert_path: str) -> str:
    """Return the SHA-1 thumbprint of a PEM cert (uppercase hex, no colons)."""
    try:
        from cryptography import x509 as _x509
        from cryptography.hazmat.primitives import hashes as _hashes
        with open(cert_path, "rb") as f:
            cert = _x509.load_pem_x509_certificate(f.read())
        return cert.fingerprint(_hashes.SHA1()).hex().upper()
    except Exception:
        return ""


# ─────────────────────────────────────────────────────────────────────────────
# macOS
# ─────────────────────────────────────────────────────────────────────────────

def _install_macos(cert_path: str, cert_name: str) -> bool:
    """Install into the login keychain (per-user, no sudo required)."""
    login_keychain = os.path.expanduser("~/Library/Keychains/login.keychain-db")
    if not os.path.exists(login_keychain):
        login_keychain = os.path.expanduser("~/Library/Keychains/login.keychain")

    try:
        _run([
            "security", "add-trusted-cert",
            "-d", "-r", "trustRoot",
            "-k", login_keychain,
            cert_path,
        ])
        log.info("Certificate installed in macOS login keychain.")
        return True
    except (subprocess.CalledProcessError, FileNotFoundError) as exc:
        log.warning("login keychain install failed: %s. Trying system keychain (needs sudo)…", exc)

    try:
        _run([
            "sudo", "security", "add-trusted-cert",
            "-d", "-r", "trustRoot",
            "-k", "/Library/Keychains/System.keychain",
            cert_path,
        ])
        log.info("Certificate installed in macOS system keychain.")
        return True
    except (subprocess.CalledProcessError, FileNotFoundError) as exc:
        log.error("System keychain install failed: %s", exc)

    return False


def _is_trusted_macos(cert_name: str) -> bool:
    try:
        result = _run(["security", "find-certificate", "-a", "-c", cert_name])
        return bool(result.stdout.strip())
    except Exception:
        return False


# ─────────────────────────────────────────────────────────────────────────────
# Linux
# ─────────────────────────────────────────────────────────────────────────────

def _detect_linux_distro() -> str:
    """Return 'debian', 'rhel', 'arch', or 'unknown'."""
    if os.path.exists("/etc/debian_version") or os.path.exists("/etc/ubuntu"):
        return "debian"
    if os.path.exists("/etc/redhat-release") or os.path.exists("/etc/fedora-release"):
        return "rhel"
    if os.path.exists("/etc/arch-release"):
        return "arch"
    # Read /etc/os-release as fallback
    try:
        with open("/etc/os-release") as f:
            content = f.read().lower()
        if "debian" in content or "ubuntu" in content or "mint" in content:
            return "debian"
        if "fedora" in content or "rhel" in content or "centos" in content or "rocky" in content or "alma" in content:
            return "rhel"
        if "arch" in content or "manjaro" in content:
            return "arch"
    except OSError:
        pass
    return "unknown"


def _install_linux(cert_path: str, cert_name: str) -> bool:
    distro = _detect_linux_distro()
    log.info("Detected Linux distro family: %s", distro)

    installed = False

    if distro == "debian":
        dest_dir = "/usr/local/share/ca-certificates"
        dest_file = os.path.join(dest_dir, f"{cert_name.replace(' ', '_')}.crt")
        try:
            os.makedirs(dest_dir, exist_ok=True)
            shutil.copy2(cert_path, dest_file)
            _run(["update-ca-certificates"])
            log.info("Certificate installed via update-ca-certificates.")
            installed = True
        except (OSError, subprocess.CalledProcessError) as exc:
            log.warning("Debian install failed (needs sudo?): %s", exc)
            # Try with sudo
            try:
                _run(["sudo", "cp", cert_path, dest_file])
                _run(["sudo", "update-ca-certificates"])
                log.info("Certificate installed via sudo update-ca-certificates.")
                installed = True
            except (subprocess.CalledProcessError, FileNotFoundError) as exc2:
                log.error("sudo Debian install failed: %s", exc2)

    elif distro == "rhel":
        dest_dir = "/etc/pki/ca-trust/source/anchors"
        dest_file = os.path.join(dest_dir, f"{cert_name.replace(' ', '_')}.crt")
        try:
            os.makedirs(dest_dir, exist_ok=True)
            shutil.copy2(cert_path, dest_file)
            _run(["update-ca-trust", "extract"])
            log.info("Certificate installed via update-ca-trust.")
            installed = True
        except (OSError, subprocess.CalledProcessError) as exc:
            log.warning("RHEL install failed (needs sudo?): %s", exc)
            try:
                _run(["sudo", "cp", cert_path, dest_file])
                _run(["sudo", "update-ca-trust", "extract"])
                log.info("Certificate installed via sudo update-ca-trust.")
                installed = True
            except (subprocess.CalledProcessError, FileNotFoundError) as exc2:
                log.error("sudo RHEL install failed: %s", exc2)

    elif distro == "arch":
        dest_dir = "/etc/ca-certificates/trust-source/anchors"
        dest_file = os.path.join(dest_dir, f"{cert_name.replace(' ', '_')}.crt")
        try:
            os.makedirs(dest_dir, exist_ok=True)
            shutil.copy2(cert_path, dest_file)
            _run(["trust", "extract-compat"])
            log.info("Certificate installed via trust extract-compat.")
            installed = True
        except (OSError, subprocess.CalledProcessError) as exc:
            log.warning("Arch install failed (needs sudo?): %s", exc)
            try:
                _run(["sudo", "cp", cert_path, dest_file])
                _run(["sudo", "trust", "extract-compat"])
                log.info("Certificate installed via sudo trust extract-compat.")
                installed = True
            except (subprocess.CalledProcessError, FileNotFoundError) as exc2:
                log.error("sudo Arch install failed: %s", exc2)

    else:
        log.warning(
            "Unknown Linux distro. Manually install %s as a trusted root CA.", cert_path
        )

    return installed


def _is_trusted_linux(cert_path: str, cert_name: str = CERT_NAME) -> bool:
    """Check whether the cert appears in common Linux trust stores."""
    try:
        from cryptography import x509 as _x509
        from cryptography.hazmat.primitives import hashes as _hashes
    except Exception:
        return False

    try:
        with open(cert_path, "rb") as f:
            target_cert = _x509.load_pem_x509_certificate(f.read())
        target_fp = target_cert.fingerprint(_hashes.SHA1())
    except Exception:
        return False

    # First check the common anchor locations used by the installer.
    expected_name = f"{cert_name.replace(' ', '_')}.crt"
    anchor_dirs = [
        "/usr/local/share/ca-certificates",
        "/etc/pki/ca-trust/source/anchors",
        "/etc/ca-certificates/trust-source/anchors",
    ]
    for d in anchor_dirs:
        try:
            if not os.path.isdir(d):
                continue
            if expected_name in os.listdir(d):
                return True
        except OSError:
            pass

    # Fall back to scanning the system bundle files directly.
    bundle_paths = [
        "/etc/ssl/certs/ca-certificates.crt",   # Debian/Ubuntu
        "/etc/pki/tls/certs/ca-bundle.crt",     # RHEL/Fedora
        "/etc/ssl/ca-bundle.pem",               # OpenSUSE
        "/etc/ca-certificates/ca-certificates.crt",
    ]

    begin = b"-----BEGIN CERTIFICATE-----"
    end = b"-----END CERTIFICATE-----"
    for bundle in bundle_paths:
        try:
            with open(bundle, "rb") as f:
                data = f.read()
        except OSError:
            continue

        for chunk in data.split(begin):
            if end not in chunk:
                continue
            pem = begin + chunk.split(end, 1)[0] + end + b"\n"
            try:
                cert = _x509.load_pem_x509_certificate(pem)
            except Exception:
                continue
            try:
                if cert.fingerprint(_hashes.SHA1()) == target_fp:
                    return True
            except Exception:
                continue

    return False


# ─────────────────────────────────────────────────────────────────────────────
# Firefox NSS (cross-platform)
# ─────────────────────────────────────────────────────────────────────────────

def _install_firefox(cert_path: str, cert_name: str):
    """Install into all detected Firefox profile NSS databases."""
    if not _has_cmd("certutil"):
        log.debug("NSS certutil not found — skipping Firefox install.")
        return

    profile_dirs: list[str] = []
    system = platform.system()

    if system == "Windows":
        appdata = os.environ.get("APPDATA", "")
        profile_dirs += glob.glob(os.path.join(appdata, r"Mozilla\Firefox\Profiles\*"))
    elif system == "Darwin":
        profile_dirs += glob.glob(os.path.expanduser("~/Library/Application Support/Firefox/Profiles/*"))
    else:
        profile_dirs += glob.glob(os.path.expanduser("~/.mozilla/firefox/*.default*"))
        profile_dirs += glob.glob(os.path.expanduser("~/.mozilla/firefox/*.release*"))

    if not profile_dirs:
        log.debug("No Firefox profiles found.")
        return

    for profile in profile_dirs:
        db = f"sql:{profile}" if os.path.exists(os.path.join(profile, "cert9.db")) else f"dbm:{profile}"
        try:
            # Remove old entry first (ignore errors)
            _run(["certutil", "-D", "-n", cert_name, "-d", db], check=False)
            _run([
                "certutil", "-A",
                "-n", cert_name,
                "-t", "CT,,",
                "-i", cert_path,
                "-d", db,
            ])
            log.info("Installed in Firefox profile: %s", os.path.basename(profile))
        except (subprocess.CalledProcessError, FileNotFoundError) as exc:
            log.warning("Firefox profile %s: %s", os.path.basename(profile), exc)


# ─────────────────────────────────────────────────────────────────────────────
# Public API
# ─────────────────────────────────────────────────────────────────────────────

def is_ca_trusted(cert_path: str) -> bool:
    """Return True if the CA cert appears to be already installed."""
    system = platform.system()
    try:
        if system == "Windows":
            return _is_trusted_windows(cert_path)
        if system == "Darwin":
            return _is_trusted_macos(CERT_NAME)
        return _is_trusted_linux(cert_path, CERT_NAME)
    except Exception:
        return False


def install_ca(cert_path: str, cert_name: str = CERT_NAME) -> bool:
    """
    Install *cert_path* as a trusted root CA on the current platform.
    Also attempts Firefox NSS installation.

    Returns True if the system store installation succeeded.
    """
    if not os.path.exists(cert_path):
        log.error("Certificate file not found: %s", cert_path)
        return False

    system = platform.system()
    log.info("Installing CA certificate on %s…", system)

    if system == "Windows":
        ok = _install_windows(cert_path, cert_name)
    elif system == "Darwin":
        ok = _install_macos(cert_path, cert_name)
    elif system == "Linux":
        ok = _install_linux(cert_path, cert_name)
    else:
        log.error("Unsupported platform: %s", system)
        return False

    # Best-effort Firefox install on all platforms
    _install_firefox(cert_path, cert_name)

    return ok
