#!/usr/bin/env python3
"""
Abflug Launcher
Simple GUI to launch Client or Rewinger.
Run from this directory: python launcher.py
"""

import tkinter as tk
from tkinter import ttk, messagebox
import subprocess
import sys
import os
import re
import json
import webbrowser

from dpi_utils import configure_tk_dpi, get_safe_window_geometry
from config_utils import get_server_url

MIN_BUTTON_WIDTH = 30

# Constants
APP_NAME = "Abflug Launcher"
APP_VERSION = "2.6.0"

# Directory containing this launcher (and abflug_gui.py, rewinger.py, or .exe equivalents)
def _get_launcher_dir() -> str:
    """Base directory for launcher and apps. Works for Python scripts and PyInstaller .exe."""
    if getattr(sys, "frozen", False):
        return os.path.dirname(sys.executable)
    return os.path.dirname(os.path.abspath(__file__))


LAUNCHER_DIR = _get_launcher_dir()

# When running as PyInstaller .exe, we launch .exe files instead of Python scripts
IS_FROZEN = getattr(sys, "frozen", False)


def launch_app(app_name: str, script_name: str) -> None:
    """Launch the specified application in a new process."""
    if IS_FROZEN:
        # PyInstaller build: launch .exe (e.g. abflug_gui.exe, rewinger.exe)
        exe_name = script_name.replace(".py", ".exe")
        target_path = os.path.join(LAUNCHER_DIR, exe_name)
        cmd = [target_path]
    else:
        # Python script mode: launch with interpreter
        target_path = os.path.join(LAUNCHER_DIR, script_name)
        cmd = [sys.executable, target_path]

    if not os.path.isfile(target_path):
        expected = script_name.replace(".py", ".exe") if IS_FROZEN else script_name
        messagebox.showerror("Error", f"Not found: {expected}\n\nRun the launcher from the Abflug folder.")
        return
    try:
        subprocess.Popen(
            cmd,
            cwd=LAUNCHER_DIR,
            start_new_session=True,
        )
    except Exception as e:
        messagebox.showerror("Error", f"Failed to launch {app_name}:\n{str(e)}")


def smoke_test() -> bool:
    """Quick import check - copied from build_release.py smoke_test function."""
    try:
        res = subprocess.run(
            [sys.executable, "-c", "import abflug_gui; import rewinger; print('OK')"],
            cwd=LAUNCHER_DIR,
            capture_output=True,
            text=True,
            timeout=10,
        )
        if res.returncode != 0:
            return False
        return True
    except Exception:
        return False


def check_pip_exists() -> bool:
    """Check if pip is available."""
    try:
        res = subprocess.run(
            [sys.executable, "-m", "pip", "--version"],
            capture_output=True,
            text=True,
            timeout=5,
        )
        return res.returncode == 0
    except Exception:
        return False


def check_package_installed(package_name: str) -> bool:
    """Check if a package is already installed."""
    try:
        res = subprocess.run(
            [sys.executable, "-m", "pip", "show", package_name],
            capture_output=True,
            text=True,
            timeout=5,
        )
        return res.returncode == 0
    except Exception:
        return False


def install_package(package_name: str) -> tuple[bool, str]:
    """Install a package using pip. Returns (success, message)."""
    try:
        res = subprocess.run(
            [sys.executable, "-m", "pip", "install", package_name],
            capture_output=True,
            text=True,
            timeout=60,
        )
        if res.returncode != 0:
            error_msg = res.stderr or res.stdout or "Unknown error"
            return False, error_msg
        return True, "Successfully installed"
    except subprocess.TimeoutExpired:
        return False, "Installation timed out"
    except Exception as e:
        return False, str(e)


def check_prerequisites() -> None:
    """Check prerequisites and offer to install missing packages."""
    # Step 1: Check if pip exists
    if not check_pip_exists():
        messagebox.showerror(
            "Prerequisites Check Failed",
            "pip is not available on this system.\n\n"
            "Please install pip to continue with package installation."
        )
        return
    
    # Step 2: Check which packages need to be installed
    packages = ["requests", "tkintermapview"]
    missing_packages = [pkg for pkg in packages if not check_package_installed(pkg)]
    
    # Step 3: Install missing packages if any
    if missing_packages:
        missing_list = "\n".join(f"  • {pkg}" for pkg in missing_packages)
        response = messagebox.askyesno(
            "Install Required Packages",
            "The following Python packages need to be installed:\n"
            f"{missing_list}\n\n"
            "Do you want to proceed with the installation?",
            icon='question'
        )
        
        if not response:
            return
        
        # Install missing packages
        failed_packages = []
        
        for package in missing_packages:
            success, message = install_package(package)
            if not success:
                failed_packages.append(f"{package}: {message}")
        
        if failed_packages:
            error_msg = "Failed to install some packages:\n\n" + "\n".join(failed_packages)
            messagebox.showerror("Installation Failed", error_msg)
            return
        else:
            installed_list = "\n".join(f"  • {pkg}" for pkg in missing_packages)
            messagebox.showinfo(
                "Installation Successful",
                f"All packages have been successfully installed:\n{installed_list}\n\n"
                "Running final verification..."
            )
    
    # Step 4: Run smoke test (after packages are installed)
    if not smoke_test():
        messagebox.showerror(
            "Prerequisites Check Failed",
            "Smoke test failed: Could not import required modules (abflug_gui, rewinger).\n\n"
            "Please ensure all required files are present in the abflug folder."
        )
        return
    
    # All checks passed
    messagebox.showinfo(
        "Prerequisites Check",
        "All prerequisites are satisfied!\n\n"
        "✓ pip is available\n"
        "✓ requests is installed\n"
        "✓ tkintermapview is installed\n"
        "✓ Smoke test passed\n\n"
        "You're ready to use the Abflug applications."
    )


def get_local_version() -> str:
    """Extract version from launcher.py file."""
    launcher_path = os.path.join(LAUNCHER_DIR, "launcher.py")
    try:
        with open(launcher_path, "r", encoding="utf-8") as f:
            content = f.read()
        version_match = re.search(r'APP_VERSION\s*=\s*"([^"]+)"', content)
        if version_match:
            return version_match.group(1)
    except Exception:
        pass
    # Fallback to constant if file reading fails
    return APP_VERSION


def parse_version(version_str: str) -> tuple:
    """Parse version string "X.Y.Z" into tuple (X, Y, Z)."""
    try:
        parts = version_str.split('.')
        return tuple(int(p) for p in parts)
    except (ValueError, AttributeError):
        return (0, 0, 0)


def compare_versions(v1: str, v2: str) -> int:
    """
    Compare two version strings.
    Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2
    """
    t1 = parse_version(v1)
    t2 = parse_version(v2)
    if t1 < t2:
        return -1
    elif t1 > t2:
        return 1
    return 0


def get_latest_version(server_url: str, variant: str = "beta") -> dict:
    """
    Query server for latest version information.
    Returns dict with version info or None on error.
    """
    try:
        import requests
    except ImportError:
        return None
    
    try:
        url = f"{server_url.rstrip('/')}/api/client/version"
        params = {"variant": variant} if variant else {}
        response = requests.get(url, params=params, timeout=10)
        
        if response.status_code == 200:
            return response.json()
        else:
            return None
    except Exception:
        return None


def check_for_updates(show_no_update: bool = False) -> None:
    """Check for updates and show appropriate dialog."""
    # Get local version
    local_version = get_local_version()
    if not local_version:
        messagebox.showerror(
            "Update Check Failed",
            "Could not determine local version."
        )
        return
    
    # Get server URL
    server_url = get_server_url()
    
    # Query server (with timeout, so it won't block too long)
    variant = "beta"  # Could be detected from folder name or config
    latest_info = get_latest_version(server_url, variant)
    
    show_update_result(latest_info, local_version, show_no_update)


def show_update_result(latest_info: dict, local_version: str, show_no_update: bool) -> None:
    """Show the result of the update check (called from main thread)."""
    if latest_info is None:
        messagebox.showerror(
            "Update Check Failed",
            "Could not check for updates.\n\n"
            "Please check your internet connection\n"
            "and try again later."
        )
        return
    
    latest_version = latest_info.get("latest_version", "")
    download_url = latest_info.get("download_url", "")
    changelog_url = latest_info.get("changelog_url", "")
    
    if not latest_version:
        messagebox.showerror(
            "Update Check Failed",
            "Invalid response from server."
        )
        return
    
    # Compare versions
    comparison = compare_versions(local_version, latest_version)
    
    if comparison < 0:
        # Update available
        message = (
            f"Update Available!\n\n"
            f"Current version: {local_version}\n"
            f"Latest version: {latest_version}\n\n"
            f"Would you like to download the update?"
        )
        
        response = messagebox.askyesno(
            "Update Available",
            message,
            icon='question'
        )
        
        if response:
            if download_url:
                webbrowser.open(download_url)
                messagebox.showinfo(
                    "Download",
                    "Opening download page in your browser.\n\n"
                    "After downloading, extract the zip file\n"
                    "and replace your current files."
                )
            else:
                messagebox.showinfo(
                    "Update Available",
                    f"Latest version: {latest_version}\n\n"
                    "Please visit abflug.cloud to download the update."
                )
    elif comparison == 0:
        # Up to date
        if show_no_update:
            messagebox.showinfo(
                "Up to Date",
                f"You're running the latest version ({local_version}).\n\n"
                "No updates available."
            )
    else:
        # Local version is newer (shouldn't happen, but handle gracefully)
        if show_no_update:
            messagebox.showinfo(
                "Version Check",
                f"Local version: {local_version}\n"
                f"Server version: {latest_version}\n\n"
                "Your version appears to be newer than the server version."
            )

def main():
    # Create main window
    root = tk.Tk()
    # Configure DPI scaling for Windows
    scale_factor = configure_tk_dpi(root)
    safe_geometry = get_safe_window_geometry(root, 350, 450, scale_factor, center=True)
    root.title("Abflug Launcher")
    root.geometry(safe_geometry)
    root.resizable(True, True)
    
    # Configure style
    #root.configure(bg='#f0f0f0')
    
    # Header
    header = ttk.Label(
        root, 
        text="Abflug", 
        font=("Arial", 24, "bold"),
        #bg='#f0f0f0',
        #fg='#333'
    )
    header.pack(pady=30)
    
    subtitle = ttk.Label(
        root,
        text="Choose which component to launch:",
        #font=("Arial", 10),
        #bg='#f0f0f0',
        #fg='#666'
    )
    subtitle.pack(pady=5)
    
    # Button frame
    #btn_frame = tk.Frame(root, bg='#f0f0f0')
    btn_frame = ttk.Frame(root)
    btn_frame.pack(pady=20)
    
    # Client button
    client_btn = ttk.Button(
        btn_frame,
        text="🛫 Abflug Client (Position Sharing)",
        command=lambda: launch_app("Client", "abflug_gui.py"),
        width=MIN_BUTTON_WIDTH,
        #height=3,
        #font=("Arial", 11),
        #bg='#0066cc',
        #fg='white',
        #activebackground='#0052a3',
        #relief='raised',
        #cursor='hand2'
    )
    client_btn.pack(pady=10)
    
    # Rewinger button
    rewinger_btn = ttk.Button(
        btn_frame,
        text="🗺️ Rewinger (Map Visualization)",
        command=lambda: launch_app("Rewinger", "rewinger.py"),
        width=MIN_BUTTON_WIDTH,
        #height=3,
        #font=("Arial", 11),
        #bg='#00aa44',
        #fg='white',
        #activebackground='#008833',
        #relief='raised',
        #cursor='hand2'
    )
    rewinger_btn.pack(pady=10)
    
    # Check Prerequisites button (only when running as Python script - not for .exe builds)
    if not IS_FROZEN:
        prerequisites_btn = ttk.Button(
            btn_frame,
            text="✓ Check Prerequisites",
            command=check_prerequisites,
            width=MIN_BUTTON_WIDTH,
        )
        prerequisites_btn.pack(pady=10)
    
    # Check for Updates button
    updates_btn = ttk.Button(
        btn_frame,
        text="🔄 Check for Updates",
        command=lambda: check_for_updates(show_no_update=True),
        width=MIN_BUTTON_WIDTH,
    )
    updates_btn.pack(pady=10)
    
    # Footer
    footer = ttk.Label(
        root,
        text="abflug.cloud",
        font=("Arial", 8),
        #bg='#f0f0f0',
        #fg='#999'
    )
    footer.pack(side='bottom', pady=10)
    
    # Start GUI
    root.mainloop()

if __name__ == "__main__":
    main()