"""Finish the install: dependencies, then ffmpeg if this machine has none.

Run once by the installer, and safe to run again — it is also the repair
path when someone's antivirus eats a package. Nothing here is clever; the
value is that it says what it is doing, because a silent five-minute pause
after an installer finishes reads as a hang.
"""
import os
import subprocess
import sys
import urllib.request
from pathlib import Path

HERE = Path(__file__).resolve().parent
ROOT = HERE.parent
PY = ROOT / "python" / ("python.exe" if os.name == "nt" else "bin/python3")
GET_PIP = "https://bootstrap.pypa.io/get-pip.py"


LOG = ROOT / "first_run.log"


def say(msg):
    """Print AND log. The installer does not capture this output, so without
    the file a failure here is invisible — the install reports success and
    the user gets an app whose imports all fail."""
    line = f"  {msg}"
    print(line, flush=True)
    try:
        with open(LOG, "a", encoding="utf-8", errors="replace") as fh:
            fh.write(line + "\n")
    except Exception:
        pass


def have_pip() -> bool:
    return subprocess.run([str(PY), "-m", "pip", "--version"],
                          capture_output=True).returncode == 0


def bootstrap_pip():
    """The embeddable distro ships without pip on purpose."""
    say("installing the package manager...")
    tmp = ROOT / "get-pip.py"
    with urllib.request.urlopen(GET_PIP, timeout=120) as r:
        tmp.write_bytes(r.read())
    r = subprocess.run([str(PY), str(tmp), "--no-warn-script-location"],
                       text=True, capture_output=True)
    for line in ((r.stdout or "") + (r.stderr or "")).splitlines()[-6:]:
        say("get-pip: " + line[:200])
    if r.returncode != 0:
        raise RuntimeError(f"get-pip failed ({r.returncode})")
    tmp.unlink(missing_ok=True)


def enable_site():
    """Embedded Python ignores site-packages until its ._pth says otherwise,
    which is the difference between pip working and pip installing into a
    directory nothing ever reads."""
    for pth in (ROOT / "python").glob("python*._pth"):
        text = pth.read_text()
        if "import site" in text and not text.count("#import site"):
            continue
        pth.write_text(text.replace("#import site", "import site")
                       if "#import site" in text else text + "\nimport site\n")
        say(f"enabled site-packages in {pth.name}")


def main():
    print(f"\n  Setting up Clipshelf in {ROOT}\n")
    if not PY.exists():
        sys.exit(f"  the bundled interpreter is missing at {PY}")

    enable_site()
    if not have_pip():
        bootstrap_pip()

    # get-pip installs pip and nothing else. Anything in the tree that
    # ships as a source archive then fails with "Cannot import
    # setuptools.build_meta" — pywebview pulls proxy_tools, which is sdist
    # only, so this is not an edge case.
    say("installing build tools...")
    r = subprocess.run([str(PY), "-m", "pip", "install", "--no-warn-script-location",
                        "setuptools", "wheel"], text=True, capture_output=True)
    for line in ((r.stdout or "") + (r.stderr or "")).splitlines()[-4:]:
        say("pip: " + line[:200])
    if r.returncode != 0:
        say(f"could not install build tools (exit {r.returncode})")
        sys.exit(r.returncode)

    say("installing dependencies (a few minutes on first run)...")
    req = ROOT / "requirements.txt"
    # captured, not streamed: this runs under the installer, which shows no
    # console, so pip's own error was the one thing missing when this failed
    r = subprocess.run([str(PY), "-m", "pip", "install", "--no-warn-script-location",
                        "-r", str(req)], text=True, capture_output=True)
    for line in (r.stdout or "").splitlines()[-8:]:
        say("pip: " + line[:200])
    if r.returncode != 0:
        say(f"dependency install FAILED (exit {r.returncode})")
        for line in (r.stderr or "").splitlines()[-25:]:
            say("pip! " + line[:200])
        say("retry from the Start menu shortcut once the cause is fixed")
        sys.exit(r.returncode)

    # ffmpeg: only if the machine has none. It is fetched rather than bundled
    # because the builds carrying libx264 are GPL, and shipping one inside a
    # proprietary installer means taking on obligations for that binary.
    sys.path.insert(0, str(ROOT / "src"))
    try:
        from clipshelf import ffmpeg_setup
        if ffmpeg_setup.available():
            say(f"ffmpeg found at {ffmpeg_setup.find()}")
        else:
            say("downloading ffmpeg (about 80 MB)...")
            res = ffmpeg_setup.install()
            say(f"ffmpeg: {res.get('path') or res.get('error')}")
    except Exception as e:
        say(f"could not set up ffmpeg automatically ({e})")
        say("the app will offer to download it again on first launch")

    # prove it: a dependency that installs but cannot be imported is the
    # failure mode this whole file exists to avoid
    check = subprocess.run(
        [str(PY), "-c", "import fastapi, uvicorn; print('imports ok')"],
        capture_output=True, text=True)
    say((check.stdout or check.stderr).strip()[:200])
    if check.returncode != 0:
        say("dependencies installed but cannot be imported — site-packages "
            "is probably not on the interpreter's path")
        sys.exit(3)

    print("\n  Done. Clipshelf is ready.\n")


if __name__ == "__main__":
    main()
