from __future__ import annotations

import io
import logging
import shutil
import subprocess
import zipfile
from pathlib import Path

import httpx

from .paths import gost_exe_path

log = logging.getLogger(__name__)

GOST_RELEASE_ZIP = (
    "https://github.com/go-gost/gost/releases/download/v3.2.6/"
    "gost_3.2.6_windows_amd64.zip"
)


def _add_defender_exclusion(path: Path) -> None:
    """Add Windows Defender exclusion so GOST is not quarantined."""
    try:
        subprocess.run(
            ["powershell", "-NoProfile", "-NonInteractive", "-Command",
             f"Add-MpPreference -ExclusionPath '{path.parent}' -ExclusionProcess 'gost.exe' -ErrorAction SilentlyContinue"],
            capture_output=True,
            timeout=30,
            creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
        )
        log.info("Defender exclusion added for %s", path.parent)
    except Exception:
        pass  # non-fatal


def ensure_gost(target: Path | None = None) -> Path:
    exe = target or gost_exe_path()
    if exe.is_file() and exe.stat().st_size > 10_000:
        return exe
    exe.parent.mkdir(parents=True, exist_ok=True)
    # Add exclusion BEFORE downloading so Defender doesn't nuke it on write
    _add_defender_exclusion(exe)
    log.info("Downloading GOST %s", GOST_RELEASE_ZIP)
    with httpx.Client(timeout=120.0, follow_redirects=True) as c:
        r = c.get(GOST_RELEASE_ZIP)
        r.raise_for_status()
        data = r.content
    if len(data) < 64 or data[:2] != b"PK":
        raise RuntimeError("Downloaded GOST zip looks invalid (not a zip).")
    with zipfile.ZipFile(io.BytesIO(data), "r") as z:
        names = [n for n in z.namelist() if n.lower().endswith("gost.exe")]
        if not names:
            raise RuntimeError("gost.exe not found in release zip")
        with z.open(names[0]) as src, open(exe, "wb") as dst:
            shutil.copyfileobj(src, dst)
    # Add exclusion again after write in case Defender scanned during extraction
    _add_defender_exclusion(exe)
    log.info("GOST installed at %s", exe)
    return exe


def build_gost_cmd(gost: Path, listen_http: str, forwards: list[str]) -> list[str]:
    args = [str(gost), "-L", f"http://{listen_http}"]
    for f in forwards:
        args.extend(["-F", f])
    return args


def popen_no_window(args: list[str]) -> subprocess.Popen:
    # Use DEVNULL for both pipes — GOST writes logs to stderr, leaving pipes
    # unread would fill the OS buffer and deadlock the child process.
    cr = getattr(subprocess, "CREATE_NO_WINDOW", 0)
    return subprocess.Popen(
        args,
        stdin=subprocess.DEVNULL,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        creationflags=cr,
    )


def terminate_process(proc: subprocess.Popen | None) -> None:
    if proc is None:
        return
    if proc.poll() is not None:
        return
    try:
        proc.terminate()
        proc.wait(timeout=5)
    except Exception:
        try:
            proc.kill()
        except Exception:
            pass
