"""
start.py – Entry point for AgentOrchestrator.

Delegates to launcher_server.py which runs the persistent launcher (port 8001)
and manages the FastAPI backend (port 8000).

Can also be invoked directly: python start.py [--reload] [--no-watch]
"""
import os
import sys
import subprocess
import webbrowser
import time
import argparse
import urllib.request
import urllib.error

LAUNCHER_PORT = 8001
BACKEND_PORT  = 8000
LAUNCHER_LOCK_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".launcher.lock")
LAUNCHER_LOG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "launcher_debug.txt")


def _wait_for_launcher(timeout=10, interval=0.3) -> bool:
    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
        try:
            r = urllib.request.urlopen(f"http://127.0.0.1:{LAUNCHER_PORT}/api/launcher/status", timeout=2)
            if r.status == 200:
                return True
        except (urllib.error.URLError, OSError):
            pass
        time.sleep(interval)
    return False


def _launcher_running(timeout=1.0) -> bool:
    return _wait_for_launcher(timeout=timeout, interval=0.2)


def _tail_launcher_log(lines=20) -> str:
    if not os.path.isfile(LAUNCHER_LOG_FILE):
        return ""
    try:
        with open(LAUNCHER_LOG_FILE, encoding="utf-8", errors="replace") as handle:
            rows = handle.readlines()
        return "".join(rows[-lines:]).strip()
    except Exception:
        return ""


def main() -> int:
    parser = argparse.ArgumentParser(description="AgentOrchestrator launcher")
    parser.add_argument("--reload",  action="store_true", help="(ignored, kept for compat)")
    parser.add_argument("--no-watch", action="store_true", help="(ignored, kept for compat)")
    parser.parse_args()

    print("Setting up AI AgentOrchestrator...")

    if _launcher_running(timeout=2.5):
        print(f"Launcher already running on http://127.0.0.1:{LAUNCHER_PORT}")
        webbrowser.open(f"http://127.0.0.1:{LAUNCHER_PORT}")
        print("Browser opened. Reusing the existing launcher instance.")
        return 0

    if os.path.isfile(LAUNCHER_LOCK_FILE):
        print("Launcher lock detected. Waiting for existing launcher startup...")
        if _wait_for_launcher(timeout=6, interval=0.3):
            print(f"Launcher already running on http://127.0.0.1:{LAUNCHER_PORT}")
            webbrowser.open(f"http://127.0.0.1:{LAUNCHER_PORT}")
            print("Browser opened. Reusing the existing launcher instance.")
            return 0

    # Ensure dependencies are installed
    root = os.path.dirname(os.path.abspath(__file__))
    req_file = os.path.join(root, "requirements.txt")
    if os.path.exists(req_file):
        rc = subprocess.call(
            [sys.executable, "-m", "pip", "install", "-r", req_file, "--quiet"],
        )
        if rc != 0:
            print("Warning: pip install had issues (exit code %d)" % rc)

    # Start launcher_server.py as a persistent background process
    launcher_script = os.path.join(root, "launcher_server.py")
    pythonw = os.path.join(os.path.dirname(sys.executable), "pythonw.exe")
    creationflags = 0
    popen_kwargs = {"cwd": root}
    if sys.platform == "win32":
        creationflags = subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS
        popen_kwargs["creationflags"] = creationflags
        popen_kwargs["stdin"] = subprocess.DEVNULL
        popen_kwargs["stdout"] = subprocess.DEVNULL
        popen_kwargs["stderr"] = subprocess.DEVNULL
    if os.path.isfile(pythonw):
        subprocess.Popen([pythonw, launcher_script], **popen_kwargs)
    else:
        subprocess.Popen([sys.executable, launcher_script], **popen_kwargs)

    print("Waiting for launcher to start...")
    if _wait_for_launcher():
        print(f"Launcher ready on http://127.0.0.1:{LAUNCHER_PORT}")
        webbrowser.open(f"http://127.0.0.1:{LAUNCHER_PORT}")
        print("Browser opened. You can close this window — the launcher runs in the background.")
        return 0
    else:
        print("Warning: launcher did not respond in time. Falling back to direct backend start.")
        print(f"See launcher log: {LAUNCHER_LOG_FILE}")
        tail = _tail_launcher_log()
        if tail:
            print("Recent launcher log output:")
            print(tail)
        return _fallback_direct_start(root)


def _wait_for_backend(timeout=20, interval=0.5) -> bool:
    """Wait until the FastAPI backend is reachable on BACKEND_PORT."""
    import socket
    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
        try:
            with socket.create_connection(("127.0.0.1", BACKEND_PORT), timeout=1):
                return True
        except OSError:
            pass
        time.sleep(interval)
    return False


def _fallback_direct_start(root: str) -> int:
    """Start the FastAPI backend directly in the foreground as fallback."""
    print("=" * 60)
    print("  FALLBACK MODE — starting backend directly on port %d" % BACKEND_PORT)
    print("=" * 60)

    # Remove stale lock so next launcher attempt is clean
    if os.path.isfile(LAUNCHER_LOCK_FILE):
        try:
            os.remove(LAUNCHER_LOCK_FILE)
        except OSError:
            pass

    cmd = [sys.executable, "-m", "uvicorn", "backend.main:app",
           "--host", "127.0.0.1", "--port", str(BACKEND_PORT)]
    try:
        proc = subprocess.Popen(cmd, cwd=root)
    except Exception as exc:
        print(f"Failed to start backend: {exc}")
        return 1

    print("Waiting for backend to become ready...")
    if _wait_for_backend():
        url = f"http://127.0.0.1:{BACKEND_PORT}"
        print(f"Backend ready on {url}")
        webbrowser.open(url)
        print("Browser opened. Keep this window open — backend runs in the foreground.")
    else:
        print(f"Backend did not start in time. Check for errors above.")

    try:
        proc.wait()
    except KeyboardInterrupt:
        print("\nStopping backend...")
        proc.terminate()
        try:
            proc.wait(timeout=5)
        except subprocess.TimeoutExpired:
            proc.kill()
    return proc.returncode or 0


if __name__ == "__main__":
    raise SystemExit(main())