#!/usr/bin/env python3
"""Levanta DB (Docker) + backend (FastAPI) + frontend (Next.js) juntos.

Uso:  python run_dev.py
Ctrl+C corta los tres procesos.

Funciona en Linux/Mac y Windows sin depender de bash/Make: por eso se ancla
al binario dentro de backend/.venv en lugar de intentar "activar" el venv.
"""

import os
import signal
import subprocess
import sys
import time
from pathlib import Path

ROOT = Path(__file__).resolve().parent
IS_WINDOWS = sys.platform == "win32"

BACKEND_PORT = 8005
FRONTEND_PORT = 3005

VENV_BIN = ROOT / "backend" / ".venv" / ("Scripts" if IS_WINDOWS else "bin")
FASTAPI_BIN = VENV_BIN / ("fastapi.exe" if IS_WINDOWS else "fastapi")
NPM_BIN = "npm.cmd" if IS_WINDOWS else "npm"

# Cada proceso arranca en su propio grupo para poder matar también a sus hijos
# (npm -> next, uvicorn worker, etc.) y no solo al proceso padre.
if IS_WINDOWS:
    GROUP_KWARGS = {"creationflags": subprocess.CREATE_NEW_PROCESS_GROUP}
else:
    GROUP_KWARGS = {"preexec_fn": os.setsid}


def start(cmd: list[str], cwd: Path) -> subprocess.Popen:
    print(f"-> {' '.join(cmd)}  (cwd={cwd})")
    return subprocess.Popen(cmd, cwd=cwd, **GROUP_KWARGS)


def stop(proc: subprocess.Popen, name: str) -> None:
    if proc.poll() is not None:
        return
    print(f"Deteniendo {name}...")
    if IS_WINDOWS:
        proc.send_signal(signal.CTRL_BREAK_EVENT)
    else:
        os.killpg(os.getpgid(proc.pid), signal.SIGINT)
    try:
        proc.wait(timeout=10)
    except subprocess.TimeoutExpired:
        proc.kill()


def main() -> None:
    if not FASTAPI_BIN.exists():
        sys.exit(
            f"No se encontró {FASTAPI_BIN}.\n"
            "Creá el venv y las dependencias primero: cd backend && python -m venv .venv "
            "&& .venv/bin/pip install -r requirements.txt (o .venv\\Scripts\\pip.exe en Windows)."
        )

    print("Levantando base de datos (Docker)...")
    subprocess.run(["docker", "compose", "up", "-d", "db"], cwd=ROOT, check=True)

    procs = {
        "backend": start(
            [
                str(FASTAPI_BIN), "dev", "app/main.py",
                "--port", str(BACKEND_PORT),
                "--host", "0.0.0.0",  # accesible desde el celular en la misma red (dev de la app mobile)
            ],
            cwd=ROOT / "backend",
        ),
        # "--" separa los args de npm de los que recibe el script "dev" (next dev)
        "frontend": start(
            [NPM_BIN, "run", "dev", "--", "--port", str(FRONTEND_PORT)],
            cwd=ROOT / "frontend",
        ),
    }

    try:
        while True:
            for name, proc in procs.items():
                code = proc.poll()
                if code is not None:
                    print(f"{name} terminó solo (código {code}). Cerrando todo.")
                    return
            time.sleep(1)
    except KeyboardInterrupt:
        print("\nCtrl+C recibido, cerrando procesos...")
    finally:
        for name, proc in procs.items():
            stop(proc, name)


if __name__ == "__main__":
    main()
