"""SonicForge DAW — Desktop engine launcher (PyInstaller entry point).

Chay FastAPI backend duoi dang tien trinh nen (sidecar) cho ban cai Standalone
Windows (Tauri v2 shell). Tu chon port 8000-8010 (fallback neu bi chiem), tu
thoat khi tien trinh cha (Tauri) ket thuc — Graceful Shutdown.
"""
import logging
import os
import socket
import sys
import threading
import time

PORT_RANGE = (8000, 8010)
APP_DATA_DIR_NAME = "SonicForgeDAW"


def _pick_port():
    for port in range(PORT_RANGE[0], PORT_RANGE[1] + 1):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            # KHONG dung SO_REUSEADDR: tren Windows no CHO PHEP bind trung port
            # (2 engine cung 8000 -> request roi vao engine ngau nhien -> UI loi).
            # Listen socket khong can SO_REUSEADDR (TIME_WAIT chi ap dung cho
            # connection socket, khong phai listen socket).
            # Bind 0.0.0.0 (GIONG uvicorn) — kiem tra dung port ma server se
            # dung, truong hop port bi chiem tren interface khac khong sot.
            try:
                s.bind(("0.0.0.0", port))
                return port
            except OSError:
                continue
    return PORT_RANGE[0]


def _lan_ips():
    """IPv4 cua may tren LAN (bo loopback) — client browser o may khac mo app."""
    ips = []
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            s.connect(("8.8.8.8", 80))
            ip = s.getsockname()[0]
        if ip and not ip.startswith("127.") and ip not in ips:
            ips.append(ip)
    except OSError:
        pass
    try:
        for info in socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET):
            ip = info[4][0]
            if ip and not ip.startswith("127.") and ip not in ips:
                ips.append(ip)
    except OSError:
        pass
    return ips


def _parent_alive(pid):
    # Windows: os.kill(pid, 0) KHONG kiem tra ton tai — no goi
    # TerminateProcess (giai thich: moi sig khac CTRL_C/BREAK deu terminate).
    # => watchdog se GIET LUON tien trinh cha (Tauri) sau 1s dau tien.
    # Dung OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION): tra ve NULL khi
    # pid khong con ton tai.
    if os.name == "nt":
        import ctypes
        PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
        h = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
        if not h:
            return False  # tien trinh da chet
        ctypes.windll.kernel32.CloseHandle(h)
        return True
    try:
        os.kill(pid, 0)
        return True
    except OSError:
        return False


def main():
    # PHONG THU (fix ban 1.1.2): neu exe bi goi nhu 'python -c ...' — vd code
    # cu kiem tra thu vien bang subprocess.run([sys.executable, '-c', 'import X'])
    # thi frozen sys.executable = daw_engine.exe -> se chay CA ENGINE o day.
    # Thoat ngay, KHONG khoi dong server, tranh de quy spawn vo han.
    if len(sys.argv) > 1 and sys.argv[1] == "-c":
        return

    os.environ.setdefault("SF_DESKTOP", "1")

    # Log dir tao SOM de stderr co the tro vao file (xem duoi).
    log_dir = os.path.join(
        os.environ.get("APPDATA") or os.path.expanduser("~"),
        APP_DATA_DIR_NAME, "logs",
    )
    try:
        os.makedirs(log_dir, exist_ok=True)
    except OSError:
        log_dir = os.path.expanduser("~")

    # Windowed app (engine.spec console=False): Windows khong tao console ->
    # sys.stdout/sys.stderr = None -> uvicorn logging crash
    # ('NoneType' object has no attribute 'isatty'). Tro stdout/stderr vao
    # FILE (KHONG phai devnull) — neu devnull thi moi exception bi nuot im
    # lang (trieu chung 'stuck khong logs, khong loi').
    if sys.stdout is None:
        sys.stdout = open(os.path.join(log_dir, "stdout.log"), "w", encoding="utf-8")
    if sys.stderr is None:
        sys.stderr = open(os.path.join(log_dir, "stderr.log"), "w", encoding="utf-8")

    port = _pick_port()
    os.environ["SF_PORT"] = str(port)

    # Log ra file — khi dong goi console=False, stdout khong nhin thay duoc.
    try:
        logging.basicConfig(
            level=logging.INFO,
            format="%(asctime)s %(levelname)s %(name)s: %(message)s",
            handlers=[
                logging.FileHandler(os.path.join(log_dir, "engine.log"), encoding="utf-8"),
                logging.StreamHandler(),
            ],
        )
    except Exception:
        logging.basicConfig(level=logging.INFO)

    import uvicorn
    import app.main  # noqa: F401 — import tuong minh de PyInstaller bundle du package app

    # Bind 0.0.0.0: client browser o may khac tren LAN mo app qua dia chi IP
    # (http://<LAN-IP>:port) — lam viec realtime voi may chay standalone.
    # Cua so Tauri (shell) van mo http://127.0.0.1:port nhu cu.
    config = uvicorn.Config(app.main.app, host="0.0.0.0", port=port,
                            log_level="info", access_log=False)
    server = uvicorn.Server(config)

    parent_pid = os.environ.get("SF_PARENT_PID")
    if parent_pid:
        def _watchdog():
            pid = int(parent_pid)
            while True:
                time.sleep(1.0)
                if not _parent_alive(pid):
                    logging.getLogger("desktop_engine").info(
                        "Parent process exited — shutting down engine")
                    server.should_exit = True
                    return
        threading.Thread(target=_watchdog, daemon=True).start()

    logger = logging.getLogger("desktop_engine")
    logger.info("SonicForge engine listening on 0.0.0.0:%d", port)
    lan_ips = _lan_ips()
    if lan_ips:
        logger.info("LAN access — mo browser o may khac: %s",
                    "  |  ".join("http://%s:%d" % (ip, port) for ip in lan_ips))
    else:
        logger.warning("Khong phat hien dia chi LAN — client may khac khong truy cap duoc")
    server.run()


if __name__ == "__main__":
    main()
