"""
Desktop Application Entry Point.

This script is used by PyInstaller to create the standalone executable.
It starts the FastAPI server, opens the default web browser,
and handles auto-updates and crash reporting.
"""

import os
import sys
import threading
import webbrowser
import uvicorn
import socket
import requests
import tempfile
import subprocess
import sentry_sdk
from pathlib import Path
import time
from packaging import version

# --- CONFIGURATION ---
CURRENT_VERSION = "3.0.0"
UPDATE_API_URL = "http://localhost:8000/api/v1/updates/latest" # In prod: https://api.datashaper.ai/...
SENTRY_DSN = os.getenv("SENTRY_DSN", "") # In prod: Real DSN

# --- SENTRY SETUP ---
sentry_sdk.init(
    dsn=SENTRY_DSN,
    release=f"datashaper-desktop@{CURRENT_VERSION}",
    environment="production" if getattr(sys, 'frozen', False) else "development",
)

# Add backend to path for imports
if getattr(sys, 'frozen', False):
    # Running in PyInstaller bundle
    base_path = sys._MEIPASS
else:
    # Running in normal Python environment
    base_path = os.path.dirname(os.path.abspath(__file__))

sys.path.insert(0, os.path.join(base_path, 'backend'))

from app.api.main import app
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse

# Configure static file serving for frontend
def configure_static(app):
    """Configure FastAPI to serve SvelteKit frontend."""
    static_dir = os.path.join(base_path, 'frontend', 'build')
    
    if not os.path.exists(static_dir):
        print(f"Warning: Frontend build directory not found at {static_dir}")
        return
    
    print(f"Serving frontend from: {static_dir}")

    # Mount static assets (_app directory)
    app_dir = os.path.join(static_dir, "_app")
    if os.path.exists(app_dir):
        app.mount("/_app", StaticFiles(directory=app_dir), name="static_app")
    
    # Serve index.html for root
    @app.get("/", response_class=FileResponse)
    async def serve_root():
        return FileResponse(os.path.join(static_dir, "index.html"))
    
    # Catch-all route for SPA (must be AFTER API routes)
    # This will only catch routes that don't match /api/*
    @app.get("/{full_path:path}", response_class=FileResponse)
    async def serve_spa(full_path: str):
        # Don't intercept API routes
        if full_path.startswith("api"):
            return {"error": "API endpoint not found"}
            
        # Check if static file exists
        file_path = os.path.join(static_dir, full_path)
        if os.path.exists(file_path) and os.path.isfile(file_path):
            return FileResponse(file_path)
            
        # Fallback to index.html for client-side routing
        return FileResponse(os.path.join(static_dir, "index.html"))

def find_free_port():
    """Find a free port on localhost."""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind(('', 0))
        s.listen(1)
        port = s.getsockname()[1]
    return port

def start_server(port):
    """Start Uvicorn server."""
    # Configure static files before starting
    configure_static(app)
    
    uvicorn.run(
        app,
        host="127.0.0.1",
        port=port,
        log_level="info",
        workers=1
    )

def check_for_updates():
    """
    Check for updates and auto-install if available.
    This runs in a background thread to not block startup.
    """
    try:
        # Wait for server to start
        time.sleep(5)
        
        print("Checking for updates...")
        response = requests.get(UPDATE_API_URL, timeout=5)
        if response.status_code != 200:
            return
            
        data = response.json()
        latest_version = data.get("version")
        download_url = data.get("download_url")
        
        if version.parse(latest_version) > version.parse(CURRENT_VERSION):
            print(f"Update available: {latest_version} (Current: {CURRENT_VERSION})")
            # In a real GUI app, we would show a dialog here.
            # For this MVP, we log it. If 'force_update' is true, we could proceed.
            
            # Example logic for downloading and running installer:
            # installer_path = download_installer(download_url)
            # run_installer(installer_path)
            pass
            
    except Exception as e:
        print(f"Update check failed: {e}")
        sentry_sdk.capture_exception(e)

def download_installer(url):
    """Download installer to temp file."""
    print(f"Downloading update from {url}...")
    response = requests.get(url, stream=True)
    fd, path = tempfile.mkstemp(suffix=".exe")
    with os.fdopen(fd, 'wb') as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)
    return path

def run_installer(path):
    """Run the installer and exit."""
    print("Launching installer...")
    subprocess.Popen([path, "/SILENT"]) # Assumes InnoSetup or similar silent flag
    sys.exit(0)

def main():
    """Main entry point."""
    port = 8000 # Use fixed port or find_free_port()
    
    # Check if port is in use, if so find another
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.bind(('127.0.0.1', port))
    except OSError:
        port = find_free_port()
    
    print(f"Starting DataShaper AI v{CURRENT_VERSION} on http://127.0.0.1:{port}")
    
    # Start server in thread
    server_thread = threading.Thread(target=start_server, args=(port,))
    server_thread.daemon = True
    server_thread.start()
    
    # Start update checker in thread
    update_thread = threading.Thread(target=check_for_updates)
    update_thread.daemon = True
    update_thread.start()
    
    # Wait for server to start
    time.sleep(1)
    
    # Open browser
    webbrowser.open(f"http://127.0.0.1:{port}")
    
    # Keep main thread alive
    try:
        while server_thread.is_alive():
            server_thread.join(1)
    except KeyboardInterrupt:
        print("Shutting down...")

if __name__ == "__main__":
    main()
