# pipeline/server/web_utils.py
from __future__ import annotations # Delays annotation evaluation, allowing modern 3.10+ type syntax and forward references in older Python versions 3.8 and 3.9
import webbrowser
import shutil
import subprocess
import time
import socket
import uvicorn # Used for launching the server
from pathlib import Path
import requests
import threading
import os
from typing import Any,  Tuple
# --- Configuration ---
# Define the root directory for serving static files
# Assumes this script is run from the project root or the path is correctly resolved
STATIC_DIR = Path(__file__).parent.parent / "interface" / "web_gui"

# --- Browser Launch Logic ---

def launch_browser(url: str):
    """
    Attempts to launch the URL using specific platform commands first, 
    then falls back to the standard Python webbrowser, ensuring a new tab is opened.
    Includes a delay for stability.

    Uses subprocess.Popen to launch the browser in the background
    without blocking the main Python script.
    """
    
    launched = False
    
    # 1. Try Termux-specific launcher
    if shutil.which("termux-open-url"):
        try:
            print("[WEBPROMPT] Attempting launch using 'termux-open-url'...")
            # Run the command without capturing output to keep it clean
            subprocess.Popen(
                ["termux-open-url", url],
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL
            )
            launched = True
            return
        # CATCH 1: Specific Error if termux-open-url binary is not found (unlikely if shutil.which passed, but good practice)
        except FileNotFoundError:
            pass # shutil.which should prevent this, so we silently fall through
        # CATCH 2: General failure during execution (e.g., system error, bad URL format)
        except Exception as e:
            print(f"[WEBPROMPT WARNING] 'termux-open-url' failed: {e}. Falling back...")
        
    # 2. Try the explicit WSLg Microsoft Edge executable
    edge_bin = shutil.which("microsoft-edge")
    if edge_bin:
        # 1. Copy the current environment
        env = os.environ.copy()
        # 2. Add the silence flag only to this copy
        env["CHROME_LOG_LEVEL"] = "3"
        """
        Chromium log levels:
        Level,Name,Description
        0,INFO,The default. Logs everything from basic startup steps to minor warnings.
        1,WARNING,Logs things that look suspicious but aren't breaking the browser (most common).
        2,ERROR,Logs legitimate failures (like the GPU or Startup Creator errors you saw).
        3,FATAL,Only logs issues that cause the process to crash immediately.
        """
        try:
            print("[WEBPROMPT] Attempting launch using 'microsoft-edge' (WSLg)...")
            # Use Popen for non-blocking execution
            # Pass the URL as the first argument to open it in a new tab/window
            subprocess.Popen(
                [edge_bin, url, "--no-first-run", "--quiet"], # Added flags
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL,
                env=env,
                start_new_session=True # Detaches the process completely from this TTY
            )
            launched = True
            return
        except FileNotFoundError:
            pass # shutil.which should prevent this, so we silently fall through
        except Exception as e: 
            print(f"[WEBPROMPT WARNING] Direct 'microsoft-edge' launch failed: {e}. Falling back...")
            pass

    # 3. Try general Linux desktop launcher
    if shutil.which("xdg-open"):
        try:
            print("[WEBPROMPT] Attempting launch using 'xdg-open'...")
            subprocess.Popen(
                ["xdg-open", url],
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL
            )
            launched = True
            return
        except FileNotFoundError:
            pass # shutil.which should prevent this, so we silently fall through
        except Exception as e:
            print(f"[WEBPROMPT WARNING] 'xdg-open' failed: {e}. Falling back...")

    # 4. Fallback to standard Python library, for most environments.
    try:
        print("[WEBPROMPT] Attempting launch using standard Python 'webbrowser' module...")
        webbrowser.open_new_tab(url)
        launched = True
    except FileNotFoundError:
            pass # shutil.which should prevent this, so we silently fall through
    except Exception as e:
        print(f"[WEBPROMPT ERROR] Standard 'webbrowser' failed: {e}. Please manually open the URL.")

    # Add a brief delay after a successful launch for OS stability
    if launched:
        time.sleep(0.5)

def find_open_port(start_port: int = 8082, max_port: int = 8100) -> int:
    """
    Finds an available TCP port starting from `start_port` up to `max_port`.
    Returns the first available port.
    """
    for port in range(start_port, max_port + 1):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            try:
                s.bind(("127.0.0.1", port))
                s.close()
                return port
            except OSError:
                continue
    raise RuntimeError(f"No available port found between {start_port} and {max_port}.")

# --- 1. Serve Static Files ---

def launch_server_for_web_gui(app, host: str = "127.0.0.1", port: int = 8082):
    
    """Launches the server using uvicorn."""

    try:
        port = find_open_port(port, port + 50)
    except RuntimeError as e:
        raise RuntimeError(f"Failed to start server: {e}")
    
    url = f"http://{host}:{port}"
    print(f"Starting Generalized Web Server at {url}")
    
    # --- TEMPORARY THREAD TO LAUNCH BROWSER ---
    # Since uvicorn.run is blocking, we must launch the browser in a new thread
    # and use polling to wait for the server to be ready.
    def launch_browser_when_ready(target_url):
        MAX_WAIT_SECONDS = 5
        POLL_INTERVAL_SECONDS = 0.2
        server_ready = False
        
        for i in range(int(MAX_WAIT_SECONDS / POLL_INTERVAL_SECONDS)):
            if is_server_running(target_url):
                server_ready = True
                break
            time.sleep(POLL_INTERVAL_SECONDS)
        
        try:
            if server_ready:
                print("[WEBPROMPT] Server confirmed responsive. Launching browser.")
                launch_browser(target_url)
            else:
                print(f"[WEBPROMPT WARNING] Server not responsive after {MAX_WAIT_SECONDS}s. Please manually open the URL: {target_url}")
        except Exception:
            print("Could not launch browser automatically. Open the URL manually.")
            
    # Start the non-blocking browser launcher immediately
    threading.Thread(target=launch_browser_when_ready, args=(url,), daemon=True).start()
    # ------------------------------------------

    # Start the server (runs until interrupted) - THIS IS THE BLOCKING CALL
    uvicorn.run(app, host=host, port=port)

def launch_server_for_web_gui_(app: Any, host: str = "127.0.0.1", port: int = 8082) -> Tuple[uvicorn.Server, threading.Thread]:
    """
    Launches the Uvicorn server in a background thread and returns the server 
    object and thread for manual lifecycle management.
    """
    
    try:
        # Note: 'app' must be passed as the application import path or object
        port = find_open_port(port, port + 50)
    except RuntimeError as e:
        print(f"Error finding open port: {e}")
        # Use a dummy tuple to indicate failure, or raise
        raise RuntimeError(f"Failed to start server: {e}")
        
    
    # 1. Instantiate Configuration
    # We explicitly set the `loop` and `http` to 'asyncio' and 'h11' for reliability
    # The `workers=1` ensures it runs in the same thread environment we control
    config = uvicorn.Config(
        app, 
        host=host, 
        port=port, 
        log_level="info", 
        loop="asyncio", 
        http="h11",
        workers=1 # Must be 1 when running manually in a separate thread
    )

    
    # 2. Instantiate Server
    server = uvicorn.Server(config=config)
    
    # 3. Launch Server in a Thread
    # We use server.run() as the target for the thread.
    server_thread = threading.Thread(target=server.run, daemon=True)
    server_thread.start()
    
    # 4. Launch Browser (After starting the thread)
    
    host_port_str = f"{host}:{port}"
    url = f"http://{host_port_str}"
    MAX_WAIT_SECONDS = 5
    POLL_INTERVAL_SECONDS = 0.2

    print(f"Starting Generalized Web Server at {url}")  
    
    # --- Server Polling Logic (Replaces time.sleep(1)) ---
    server_ready = False
    for i in range(int(MAX_WAIT_SECONDS / POLL_INTERVAL_SECONDS)):
        if is_server_running(url):
            server_ready = True
            break
        time.sleep(POLL_INTERVAL_SECONDS)
    # --- End Polling Logic ---

    try:
        if server_ready:
            print("[WEBPROMPT] Server confirmed responsive. Launching browser.")
            launch_browser(url)
        else:
            print(f"[WEBPROMPT WARNING] Server did not become responsive after {MAX_WAIT_SECONDS}s. Please manually open the URL: {url}")
            
    except Exception:
        print("Could not launch browser automatically. Open the URL manually.")
        
    # Return the control objects
    return server, server_thread # Tuple[uvicorn.Server, threading.Thread]

# --- Helper to check server status ---
def is_server_running(url: str) -> bool:
    """Check if the server at the given URL is responsive."""
    try:
        # A lightweight HEAD request to the base URL
        requests.head(url, timeout=1.0)
        return True
    except requests.exceptions.RequestException:
        return False
