"""Launcher — Super-simple local app that starts the ComfyUI proxy."""

from __future__ import annotations

import asyncio
import os
import signal
import sys
from pathlib import Path
from typing import Optional

IS_WINDOWS = sys.platform.startswith("win") or os.name == "nt"

from fastapi import FastAPI, Request
from fastapi.responses import FileResponse, JSONResponse

ROOT_DIR = Path(__file__).resolve().parent.parent
ENV_FILE = ROOT_DIR / ".env"
PROXY_PY = ROOT_DIR / "proxy" / "fal_comfy_server.py"
VENV_PY = ROOT_DIR / ".venv" / "bin" / "python"
if not VENV_PY.exists():
    VENV_PY = ROOT_DIR / ".venv" / "Scripts" / "python.exe"  # windows

UI_DIR = Path(__file__).parent
app = FastAPI()

# ═══ State: one proxy subprocess at a time ═══
_proxy: Optional[asyncio.subprocess.Process] = None
_log_buffer: list[str] = []
_MAX_LOG_LINES = 300


def read_env() -> dict:
    if not ENV_FILE.exists():
        return {}
    out = {}
    for ln in ENV_FILE.read_text().splitlines():
        if "=" in ln and not ln.startswith("#"):
            k, v = ln.split("=", 1)
            out[k.strip()] = v.strip()
    return out


def write_env(env: dict):
    lines = [f"{k}={v}" for k, v in env.items() if v]
    ENV_FILE.write_text("\n".join(lines) + "\n")


@app.get("/api/status")
async def status():
    running = _proxy is not None and _proxy.returncode is None
    env = read_env()
    return {
        "running": running,
        "has_key": bool(env.get("FAL_KEY")),
        "has_endpoint": bool(env.get("FAL_COMFY_ENDPOINT")),
        "endpoint": env.get("FAL_COMFY_ENDPOINT", ""),
        "comfy_url": "http://localhost:8188" if running else "",
    }


@app.post("/api/settings")
async def save_settings(request: Request):
    """Save key/endpoint changes. If a proxy is currently running it MUST be
    stopped — otherwise it would keep talking to the old endpoint and the user
    sees stale ComfyUI behavior or an Idle/shutdown state."""
    global _proxy
    body = await request.json()
    env = read_env()
    if body.get("fal_key"):
        env["FAL_KEY"] = body["fal_key"].strip()
    if body.get("endpoint"):
        env["FAL_COMFY_ENDPOINT"] = body["endpoint"].strip()
    write_env(env)

    # Stop any running proxy so the next Start uses the fresh env
    if _proxy and _proxy.returncode is None:
        try:
            if IS_WINDOWS:
                _proxy.terminate()
            else:
                _proxy.send_signal(signal.SIGINT)
            try:
                await asyncio.wait_for(_proxy.wait(), timeout=4)
            except asyncio.TimeoutError:
                _proxy.kill()
                await _proxy.wait()
            _log_buffer.append("Settings changed — proxy stopped. Click Start to use the new endpoint.")
        except Exception as e:
            _log_buffer.append(f"Could not stop proxy cleanly: {e}")
    return {"ok": True}


async def _drain_stdout():
    assert _proxy and _proxy.stdout
    async for line in _proxy.stdout:
        text = line.decode(errors="replace").rstrip()
        _log_buffer.append(text)
        if len(_log_buffer) > _MAX_LOG_LINES:
            del _log_buffer[: len(_log_buffer) - _MAX_LOG_LINES]


@app.post("/api/start")
async def start():
    global _proxy
    if _proxy and _proxy.returncode is None:
        return {"ok": True, "already_running": True}

    env = read_env()
    if not env.get("FAL_KEY") or not env.get("FAL_COMFY_ENDPOINT"):
        return JSONResponse({"error": "FAL_KEY and FAL_COMFY_ENDPOINT required"}, status_code=400)

    _log_buffer.clear()
    _log_buffer.append("Starting ComfyUI proxy…")

    _proxy = await asyncio.create_subprocess_exec(
        str(VENV_PY),
        str(PROXY_PY),
        cwd=str(ROOT_DIR),
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.STDOUT,
        env={**os.environ, **env},
    )
    asyncio.create_task(_drain_stdout())
    return {"ok": True}


@app.post("/api/stop")
async def stop():
    global _proxy
    if not _proxy or _proxy.returncode is not None:
        return {"ok": True, "already_stopped": True}
    try:
        if IS_WINDOWS:
            _proxy.terminate()
        else:
            _proxy.send_signal(signal.SIGINT)
        try:
            await asyncio.wait_for(_proxy.wait(), timeout=5)
        except asyncio.TimeoutError:
            _proxy.kill()
            await _proxy.wait()
    finally:
        _log_buffer.append("Stopped.")
    return {"ok": True}


@app.get("/api/logs")
async def logs():
    return {"lines": _log_buffer[-150:]}


# ═══ Static UI ═══

@app.get("/")
async def index():
    return FileResponse(UI_DIR / "index.html")


@app.get("/app.js")
async def app_js():
    return FileResponse(UI_DIR / "app.js")


@app.get("/style.css")
async def style_css():
    return FileResponse(UI_DIR / "style.css")


if __name__ == "__main__":
    import atexit
    import threading
    import time
    import webbrowser

    # Write PID file so the launcher script stops only THIS session
    pid_file = ROOT_DIR / ".launcher.pid"
    pid_file.write_text(str(os.getpid()))
    atexit.register(lambda: pid_file.unlink(missing_ok=True))

    def open_browser():
        time.sleep(1.2)
        webbrowser.open("http://localhost:9998")

    threading.Thread(target=open_browser, daemon=True).start()

    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=9998, log_level="warning")
