#!/usr/bin/env python3
"""Build automation script for HAD Digital MVP.

Packages the application into a zero-dependency standalone Windows executable (.exe)
using PyInstaller. Supports both single-file (.exe) and directory bundle modes.

Usage:
    python build.py [--mode {onefile,onedir}] [--clean] [--verify]
"""

import argparse
import os
import shutil
import subprocess
import sys
import time
from pathlib import Path

# Paths relative to project root
current_dir = Path(__file__).resolve().parent
if current_dir.name in ("04_Build", "05_Test"):
    PROJECT_ROOT = current_dir.parent
elif current_dir.name == "explorer_survey_2":
    PROJECT_ROOT = current_dir.parent.parent
else:
    PROJECT_ROOT = current_dir

MVP_DIR = PROJECT_ROOT / "MVP"
BUILD_DIR = PROJECT_ROOT / "04_Build" / "build"
DIST_DIR = PROJECT_ROOT / "04_Build" / "dist"
VERSION_FILE = PROJECT_ROOT / "MVP" / "VERSION.txt"


def print_banner():
    print("""
====================================================================
           HAD Digital MVP — Automated Build System
      Zero-Dependency Standalone Windows Executable (.exe)
====================================================================
""")


def check_prerequisites():
    """Ensure required tools and source files exist."""
    print("[1/5] Checking build prerequisites...")
    
    # Check Python version
    if sys.version_info < (3, 10):
        print(f"[-] ERROR: Python 3.10+ required. Current version: {sys.version}")
        sys.exit(1)
    print(f"  [+] Python version: {sys.version.split()[0]} (OK)")

    # Check PyInstaller
    try:
        res = subprocess.run(
            [sys.executable, "-m", "PyInstaller", "--version"],
            capture_output=True,
            text=True,
            check=True
        )
        print(f"  [+] PyInstaller version: {res.stdout.strip()} (OK)")
    except (subprocess.SubprocessError, FileNotFoundError):
        print("[-] ERROR: PyInstaller is not installed. Run: pip install pyinstaller")
        sys.exit(1)

    # Check critical source files
    critical_files = [
        MVP_DIR / "app.py",
        MVP_DIR / "database.py",
        MVP_DIR / "config_manager.py",
        MVP_DIR / "user_store.py",
        MVP_DIR / "ctcae_engine.py",
        MVP_DIR / "alert_engine.py",
        MVP_DIR / "data" / "ctcae_rules.json",
        MVP_DIR / "static" / "index.html",
        MVP_DIR / "static" / "app.css",
        MVP_DIR / "static" / "app.js",
    ]
    for p in critical_files:
        if not p.exists():
            print(f"[-] ERROR: Missing required source file: {p}")
            sys.exit(1)
    print("  [+] All required source files present (OK)")


def clean_artifacts():
    """Remove previous build outputs."""
    print("[2/5] Cleaning prior build artifacts...")
    for target in [BUILD_DIR, DIST_DIR, MVP_DIR / "build", MVP_DIR / "dist"]:
        if target.exists():
            print(f"  [-] Removing {target}")
            shutil.rmtree(target, ignore_errors=True)


def stamp_version():
    """Read or create VERSION.txt metadata."""
    print("[3/5] Stamping version metadata...")
    version = "1.0.0"
    if VERSION_FILE.exists():
        try:
            version = VERSION_FILE.read_text(encoding="utf-8").strip()
        except OSError:
            pass
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime())
    print(f"  [+] Version: {version} | Built: {timestamp}")
    return version


def run_pyinstaller(mode="onefile"):
    """Execute PyInstaller build."""
    print(f"[4/5] Running PyInstaller packaging in '{mode}' mode...")
    
    DIST_DIR.mkdir(parents=True, exist_ok=True)
    BUILD_DIR.mkdir(parents=True, exist_ok=True)

    # Data folders to bundle into read-only package
    static_data = f"{MVP_DIR / 'static'};static"
    guidance_data = f"{MVP_DIR / 'guidance'};guidance"
    rules_data = f"{MVP_DIR / 'data' / 'ctcae_rules.json'};data"

    cmd = [
        sys.executable,
        "-m",
        "PyInstaller",
        "--noconfirm",
        "--name", "HAD_Digital",
        f"--workpath={BUILD_DIR}",
        f"--distpath={DIST_DIR}",
        f"--paths={MVP_DIR}",
        f"--add-data={static_data}",
        f"--add-data={guidance_data}",
        f"--add-data={rules_data}",
        "--hidden-import=adapters",
        "--hidden-import=adapters.base",
        "--hidden-import=adapters.stub",
        "--hidden-import=adapters.glm",
        "--hidden-import=http.server",
        "--hidden-import=sqlite3",
        "--exclude-module=tkinter",
        "--exclude-module=matplotlib",
        "--exclude-module=scipy",
        "--exclude-module=numpy",
        "--exclude-module=pandas",
        "--exclude-module=openpyxl",
        "--exclude-module=PIL",
        "--exclude-module=pytest",
        "--exclude-module=playwright",
        "--exclude-module=boto3",
        "--exclude-module=botocore",
        "--exclude-module=torch",
        "--console",  # Keep console enabled for server diagnostic logs
    ]

    if mode == "onefile":
        cmd.append("--onefile")
    else:
        cmd.append("--onedir")

    cmd.append(str(MVP_DIR / "app.py"))

    print(f"  [>] Command: {' '.join(cmd)}")
    start_time = time.time()
    result = subprocess.run(cmd)
    elapsed = time.time() - start_time

    if result.returncode != 0:
        print(f"[-] ERROR: PyInstaller failed with return code {result.returncode}")
        sys.exit(result.returncode)

    print(f"  [+] Packaging finished in {elapsed:.1f}s")


def verify_output(mode="onefile"):
    """Verify generated artifacts."""
    print("[5/5] Verifying generated build output...")
    if mode == "onefile":
        exe_path = DIST_DIR / "HAD_Digital.exe"
        if not exe_path.exists():
            print(f"[-] ERROR: Expected output executable not found: {exe_path}")
            sys.exit(1)
        size_mb = exe_path.stat().st_size / (1024 * 1024)
        print(f"  [+] Standalone Executable: {exe_path}")
        print(f"  [+] Binary Size: {size_mb:.2f} MB")
        print("  [+] Mode: Single standalone executable (.exe) with zero host dependencies")
    else:
        bundle_dir = DIST_DIR / "HAD_Digital"
        exe_path = bundle_dir / "HAD_Digital.exe"
        if not exe_path.exists():
            print(f"[-] ERROR: Expected bundle executable not found: {exe_path}")
            sys.exit(1)
        print(f"  [+] Directory Bundle: {bundle_dir}")
        print(f"  [+] Executable: {exe_path}")

    print("\n[SUCCESS] Build completed successfully!")


def main():
    parser = argparse.ArgumentParser(description="Build standalone Windows executable for HAD Digital MVP")
    parser.add_argument("--mode", choices=["onefile", "onedir"], default="onefile",
                        help="Packaging mode: 'onefile' (single .exe, default) or 'onedir' (folder bundle)")
    parser.add_argument("--clean", action="store_true", help="Clean build directories before packaging")
    parser.add_argument("--verify", action="store_true", help="Run programmatic verification test after build")
    args = parser.parse_args()

    print_banner()
    check_prerequisites()
    if args.clean:
        clean_artifacts()
    stamp_version()
    run_pyinstaller(mode=args.mode)
    verify_output(mode=args.mode)

    if args.verify:
        print("\n[*] Running verification script...")
        verify_script = PROJECT_ROOT / "05_Test" / "verify_mvp.py"
        if not verify_script.exists():
            verify_script = PROJECT_ROOT / ".agents" / "explorer_survey_2" / "proposed_verify.py"
        if verify_script.exists():
            exe_target = DIST_DIR / ("HAD_Digital.exe" if args.mode == "onefile" else "HAD_Digital/HAD_Digital.exe")
            subprocess.run([sys.executable, str(verify_script), "--exe", str(exe_target)])
        else:
            print("  [-] Verification script not found, skipping automated verification.")


if __name__ == "__main__":
    main()
