#!/usr/bin/env python3
"""Clipshelf launcher.

    python run.py              app window (falls back to your browser)
    python run.py --browser    server + browser, no window
    python run.py --headless   server only (no window)
    python run.py --lan        also listen on the network (see the warning)
    python run.py --selftest   check this machine and print a report

No terminal questions: an unconfigured install boots straight into the
in-app setup wizard.
"""
import os
import shutil
import sys
import time
import webbrowser
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent.parent   # install root
os.chdir(ROOT)
APP_NAME = "Clipshelf"


def _quiet_subprocesses():
    """Windows: child processes (ffmpeg, ffprobe, node) each pop a console
    window when the app itself has none. There are ~30 call sites, so set
    the flag once here instead of threading it through all of them."""
    if os.name != "nt":
        return
    import subprocess
    CREATE_NO_WINDOW = 0x08000000
    _run, _popen = subprocess.run, subprocess.Popen

    def run(*a, **kw):
        kw.setdefault("creationflags", CREATE_NO_WINDOW)
        return _run(*a, **kw)

    class Popen(_popen):
        def __init__(self, *a, **kw):
            kw.setdefault("creationflags", CREATE_NO_WINDOW)
            super().__init__(*a, **kw)

    subprocess.run = run
    subprocess.Popen = Popen


def _mark_running(url: str):
    """A file the launcher script can poll: proof the server actually came
    up. Without it a windowed launch that dies is completely silent."""
    from clipshelf.config import ROOT as _R
    try:
        (_R / ".running").write_text(url, encoding="utf-8")
    except Exception:
        pass


def _clear_running():
    from clipshelf.config import ROOT as _R
    try:
        (_R / ".running").unlink(missing_ok=True)
    except Exception:
        pass


def check_deps() -> list:
    """Hard requirements only. Everything optional degrades in-app."""
    problems = []
    if not shutil.which("ffmpeg") or not shutil.which("ffprobe"):
        problems.append(
            "ffmpeg is missing — install it and make sure it's on PATH:\n"
            "    Windows:  winget install ffmpeg   (then reopen this window)\n"
            "    macOS:    brew install ffmpeg\n"
            "    Linux:    sudo apt install ffmpeg")
    try:
        import fastapi  # noqa: F401
        import uvicorn  # noqa: F401
    except ImportError:
        problems.append("python deps missing — run:  pip install -r requirements.txt")
    return problems


def main():
    argv = set(sys.argv[1:])
    # Before anything else, including --selftest: an ffmpeg we installed
    # ourselves lives in the data dir, not on PATH, and every entry point
    # needs to see it. Doing this in the app's startup handler meant the
    # self-test — the one tool whose job is reporting what is missing —
    # reported ffmpeg missing on a machine where we had just installed it.
    try:
        from clipshelf import ffmpeg_setup
        ffmpeg_setup.ensure_on_path()
    except Exception:
        pass

    if "--selftest" in argv:
        from clipshelf.selftest import main as selftest
        sys.exit(selftest())
    problems = check_deps()
    if problems:
        print(f"{APP_NAME} can't start yet:\n")
        for p in problems:
            print(p + "\n")
        input("press enter to close…")
        sys.exit(1)

    _quiet_subprocesses()
    from clipshelf import desktop
    from clipshelf.config import CFG, PORT, configured

    if not desktop.acquire_single_instance(PORT):
        # already running: surface that instance instead of fighting it
        print(f"{APP_NAME} is already running — opening it.")
        webbrowser.open(f"http://127.0.0.1:{PORT}/")
        sys.exit(0)

    if not shutil.which(os.getenv("NODE", "node")):
        print("[!] Node.js not found — editing works, video EXPORTS are disabled.")

    url = f"http://127.0.0.1:{PORT}/"
    print(f"\n{APP_NAME} running at {url}")
    if not configured():
        print("first run — the setup wizard will open in the app.\n")

    lan = "--lan" in argv
    if lan:
        os.environ["CLIPSHELF_LAN"] = "1"
        # Everything except GSI ingest is unauthenticated — this is for a
        # trusted home network (reaching the UI from a laptop, or a remote
        # hand debugging an install), not the open internet.
        from clipshelf import lanauth
        print("[!] listening on the NETWORK, not just this machine.")
        print(f"    Other devices must enter the code: {lanauth.lan_code(CFG['secret'])}")
        import socket as _s
        try:
            ip = _s.gethostbyname(_s.gethostname())
            print(f"    http://{ip}:{PORT}/\n")
        except Exception:
            print()
    desktop.serve_in_thread(PORT, host="0.0.0.0" if lan else "127.0.0.1")
    if desktop._wait_for_server(f"http://127.0.0.1:{PORT}"):
        _mark_running(url)
    else:
        print("[!] the server did not start — see the messages above")
        _clear_running()
        sys.exit(1)

    if "--headless" in argv:
        print("headless mode — Ctrl+C to stop\n")
        try:
            while True:
                time.sleep(3600)
        except KeyboardInterrupt:
            return
        finally:
            _clear_running()

    if "--browser" not in argv and desktop.run(PORT, APP_NAME):
        _clear_running()
        return       # window closed via tray Quit

    # browser mode (explicit, or no GUI toolkit available)
    if desktop._wait_for_server(url.rstrip("/")):
        try:
            webbrowser.open(url)
        except Exception:
            pass
    print("running in your browser — close this window to stop the app.\n")
    try:
        while True:
            time.sleep(3600)
    except KeyboardInterrupt:
        pass
    finally:
        _clear_running()


if __name__ == "__main__":
    main()
