#!/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:
    """Only what makes serving the UI impossible.

    ffmpeg is deliberately NOT in here. The app ships a one-click installer
    for it (POST /api/ffmpeg/install) and reports it on the dashboard, so
    refusing to open the window over the one problem the app can fix itself
    left the user with no window AND no button to fix it with."""
    problems = []
    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 _tell_the_user(msg: str):
    """Say it somewhere a windowed launch can actually be seen.

    This used to be a bare input("press enter to close"). Under pythonw.exe
    — which is what every Start-menu shortcut uses — sys.stdin is None, so
    input() RAISED instead of pausing and the process died on the spot: no
    window, no message, nothing. The one path whose entire job is to explain
    what went wrong was the one guaranteed to explain nothing."""
    print(msg, flush=True)
    try:
        if sys.stdin is not None and sys.stdin.isatty():
            input("press enter to close...")
            return
    except Exception:
        pass
    if os.name == "nt":
        try:
            import ctypes
            ctypes.windll.user32.MessageBoxW(
                None, msg, f"{APP_NAME} - can't start", 0x10)
        except Exception:
            pass


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:
        _tell_the_user(f"{APP_NAME} can't start yet:\n\n" + "\n\n".join(problems))
        sys.exit(1)

    # missing, but not fatal: the app opens and offers to install it
    if not (shutil.which("ffmpeg") and shutil.which("ffprobe")):
        print("[!] ffmpeg not found — cutting and exports stay off until it is. "
              "Settings > Problems has a one-click install.", flush=True)

    _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()
