#!/usr/bin/env python3
"""
Build script for creating cross-platform executables of MbetterClient using PyInstaller
"""

import os
import sys
import shutil
import platform
import subprocess
from pathlib import Path
from typing import List, Dict, Any

# Build configuration
BUILD_CONFIG = {
    'app_name': 'MbetterClient',
    'app_version': '1.2.11',
    'description': 'Cross-platform multimedia client application',
    'author': 'MBetter Team',
    'entry_point': 'main.py',
    'icon_file': 'assets/icon.ico' if platform.system() == 'Windows' else 'assets/icon.png',
    'console': False,  # Set to True for debugging
    'one_file': True,  # Create single executable
    'clean_build': True,
    'upx': False,  # UPX compression (set to True if UPX is available)
}

# Platform-specific configurations
PLATFORM_CONFIG = {
    'Windows': {
        'executable_name': BUILD_CONFIG['app_name'] + '.exe',
        'icon_ext': '.ico',
        'additional_paths': [],
        'exclude_modules': ['tkinter', 'unittest', 'doctest', 'pdb', 'PyQt5', 'PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtWidgets', 'PyQt5.QtMultimedia', 'PyQt5.QtMultimediaWidgets'],
    },
    'Darwin': {  # macOS
        'executable_name': BUILD_CONFIG['app_name'],
        'icon_ext': '.icns',
        'additional_paths': [],
        'exclude_modules': ['tkinter', 'unittest', 'doctest', 'pdb', 'PyQt5', 'PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtWidgets', 'PyQt5.QtMultimedia', 'PyQt5.QtMultimediaWidgets'],
    },
    'Linux': {
        'executable_name': BUILD_CONFIG['app_name'],
        'icon_ext': '.png',
        'additional_paths': [],
        'exclude_modules': ['tkinter', 'unittest', 'doctest', 'pdb', 'PyQt5', 'PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtWidgets', 'PyQt5.QtMultimedia', 'PyQt5.QtMultimediaWidgets'],
    }
}


def get_project_root() -> Path:
    """Get the project root directory"""
    return Path(__file__).parent


def get_build_dir() -> Path:
    """Get the build directory"""
    return get_project_root() / 'build'


def get_dist_dir() -> Path:
    """Get the distribution directory"""
    return get_project_root() / 'dist'


def clean_build_directories():
    """Clean previous build artifacts"""
    print("🧹 Cleaning build directories...")
    
    dirs_to_clean = [get_build_dir(), get_dist_dir()]
    
    for dir_path in dirs_to_clean:
        if dir_path.exists():
            shutil.rmtree(dir_path)
            print(f"   Removed: {dir_path}")
    
    # Remove spec file if it exists
    spec_file = get_project_root() / f"{BUILD_CONFIG['app_name']}.spec"
    if spec_file.exists():
        spec_file.unlink()
        print(f"   Removed: {spec_file}")

    # Removed qt.conf cleanup for self-contained binary


def collect_data_files() -> List[tuple]:
    """Collect data files that need to be included in the build"""
    project_root = get_project_root()
    data_files = []
    
    # Include assets directory - ensure it's at the root level in the bundle
    assets_dir = project_root / 'assets'
    if assets_dir.exists():
        for file_path in assets_dir.rglob('*'):
            if file_path.is_file():
                # For assets, we want them at the root level, not nested
                # So instead of relative_path.parent, we use just 'assets'
                data_files.append((str(file_path), 'assets'))
                print(f"   📁 Including asset file: {file_path.name} ({file_path.stat().st_size} bytes) -> assets/")
    
    # Include web dashboard templates and static files
    web_templates = project_root / 'mbetterclient' / 'web_dashboard' / 'templates'
    if web_templates.exists():
        for file_path in web_templates.rglob('*'):
            if file_path.is_file():
                relative_path = file_path.relative_to(project_root)
                data_files.append((str(file_path), str(relative_path.parent)))
    
    web_static = project_root / 'mbetterclient' / 'web_dashboard' / 'static'
    if web_static.exists():
        for file_path in web_static.rglob('*'):
            if file_path.is_file():
                relative_path = file_path.relative_to(project_root)
                data_files.append((str(file_path), str(relative_path.parent)))
    
    # Include Qt player overlay HTML and resources
    qt_player_dir = project_root / 'mbetterclient' / 'qt_player'
    if qt_player_dir.exists():
        for file_path in qt_player_dir.rglob('*.html'):
            if file_path.is_file():
                relative_path = file_path.relative_to(project_root)
                data_files.append((str(file_path), str(relative_path.parent)))
        
        # Include any CSS, JS, or other web resources
        for ext in ['*.css', '*.js', '*.json']:
            for file_path in qt_player_dir.rglob(ext):
                if file_path.is_file():
                    relative_path = file_path.relative_to(project_root)
                    data_files.append((str(file_path), str(relative_path.parent)))
    
    # Include Qt player templates directory and all assets
    templates_dir = project_root / 'mbetterclient' / 'qt_player' / 'templates'
    if templates_dir.exists():
        for file_path in templates_dir.rglob('*'):
            if file_path.is_file():
                relative_path = file_path.relative_to(project_root)
                data_files.append((str(file_path), str(relative_path.parent)))
        print(f"   📁 Including templates directory: {templates_dir}")
        
        # Specifically ensure JavaScript files in templates are included
        js_files = list(templates_dir.rglob('*.js'))
        css_files = list(templates_dir.rglob('*.css'))
        print(f"   📄 Found {len(js_files)} JavaScript files in templates")
        print(f"   🎨 Found {len(css_files)} CSS files in templates")
    
    # Include any external JavaScript/CSS assets that templates might reference
    # Look for common asset directories that templates might use
    asset_patterns = ['assets', 'js', 'css', 'fonts', 'images']
    for pattern in asset_patterns:
        asset_dir = project_root / 'mbetterclient' / 'qt_player' / pattern
        if asset_dir.exists():
            for file_path in asset_dir.rglob('*'):
                if file_path.is_file():
                    relative_path = file_path.relative_to(project_root)
                    data_files.append((str(file_path), str(relative_path.parent)))
            print(f"   📁 Including asset directory: {asset_dir}")

    # Include Qt WebEngine data files and resources
    webengine_data_dirs = [
        '/usr/share/qt6/resources',
        '/usr/share/qt6/translations/qtwebengine_locales',
        '/usr/lib/x86_64-linux-gnu/qt6/libexec',
    ]

    for data_dir in webengine_data_dirs:
        if os.path.exists(data_dir):
            for root, dirs, files in os.walk(data_dir):
                for file in files:
                    full_path = os.path.join(root, file)
                    rel_path = os.path.relpath(root, '/usr/share/qt6') if 'share' in data_dir else os.path.relpath(root, '/usr/lib/x86_64-linux-gnu/qt6')
                    data_files.append((full_path, rel_path))
            print(f"   📁 Including Qt WebEngine data: {data_dir}")

    # Create qt.conf to ensure Qt uses bundled libraries
    qt_conf_content = """[Paths]
Prefix = .
Libraries = .
Plugins = plugins
"""
    qt_conf_path = project_root / 'qt.conf'
    with open(qt_conf_path, 'w') as f:
        f.write(qt_conf_content)

    # Include qt.conf in the build
    data_files.append((str(qt_conf_path), '.'))

    print("   📦 Created qt.conf for self-contained Qt configuration")
    print("   📦 Building self-contained binary with all Qt libraries included")

    return data_files


def collect_hidden_imports() -> List[str]:
    """Collect hidden imports that PyInstaller might miss"""
    hidden_imports = [
        # PyQt6 modules
        'PyQt6.QtCore',
        'PyQt6.QtGui',
        'PyQt6.QtWidgets',
        'PyQt6.QtMultimedia',
        'PyQt6.QtMultimediaWidgets',
        'PyQt6.QtWebEngineWidgets',
        'PyQt6.QtWebChannel',

        # Flask and web dependencies
        'flask',
        'flask_login',
        'flask_jwt_extended',
        'werkzeug.security',
        'jinja2',

        # SQLAlchemy
        'sqlalchemy',
        'sqlalchemy.sql.default_comparator',

        # Requests and HTTP
        'requests',
        'urllib3',

        # Logging
        'loguru',

        # File watching for template system
        'watchdog',
        'watchdog.observers',
        'watchdog.events',

        # Other dependencies
        'packaging',
        'pkg_resources',

        # Barcode generation
        'barcode',
        'barcode.codex',
        'barcode.ean',
        'barcode.isxn',
    ]

    # Conditionally add ffmpeg module if available
    try:
        import ffmpeg
        hidden_imports.append('ffmpeg')
        print("   📦 Added ffmpeg to hidden imports")
    except ImportError:
        print("   ⚠️  ffmpeg-python not available, skipping ffmpeg imports")

    # Conditionally add Qt D-Bus support if available
    try:
        import PyQt6.QtDBus
        hidden_imports.append('PyQt6.QtDBus')
        print("   📦 Added PyQt6.QtDBus to hidden imports")
    except ImportError:
        print("   ⚠️  PyQt6.QtDBus not available, skipping D-Bus imports")

    # Add barcode submodules conditionally
    try:
        import barcode.isbn
        hidden_imports.extend(['barcode.isbn', 'barcode.code39', 'barcode.code128', 'barcode.pzn'])
        print("   📦 Added barcode submodules to hidden imports")
    except ImportError:
        print("   ⚠️  Some barcode submodules not available, skipping")

    # Conditionally add D-Bus modules if available
    try:
        import dbus
        dbus_modules = [
            'dbus',
            'dbus.mainloop',
            'dbus.mainloop.glib',
        ]
        hidden_imports.extend(dbus_modules)
        print(f"   📦 Added {len(dbus_modules)} dbus modules to hidden imports")
    except ImportError:
        print("   ⚠️  dbus not available, skipping dbus imports")

    return hidden_imports


def get_platform_config() -> Dict[str, Any]:
    """Get platform-specific configuration"""
    system = platform.system()
    return PLATFORM_CONFIG.get(system, PLATFORM_CONFIG['Linux'])


def create_icon_file():
    """Create or copy icon file for the platform"""
    project_root = get_project_root()
    platform_config = get_platform_config()
    
    icon_source = project_root / 'assets' / 'icon.png'  # Default source
    icon_target = project_root / 'assets' / f"icon{platform_config['icon_ext']}"
    
    # Create assets directory if it doesn't exist
    (project_root / 'assets').mkdir(exist_ok=True)
    
    # Create a simple icon if none exists
    if not icon_source.exists() and not icon_target.exists():
        print("📦 Creating default icon...")
        try:
            # Try to create a simple icon using PIL if available
            from PIL import Image, ImageDraw
            
            # Create a simple 256x256 icon
            img = Image.new('RGBA', (256, 256), (70, 130, 180, 255))  # Steel blue
            draw = ImageDraw.Draw(img)
            
            # Draw a simple play button
            points = [(80, 60), (180, 128), (80, 196)]
            draw.polygon(points, fill=(255, 255, 255, 255))
            
            # Save as PNG
            img.save(icon_source)
            print(f"   Created: {icon_source}")
            
        except ImportError:
            # Create a simple text-based icon file (fallback)
            with open(icon_source, 'wb') as f:
                # This is just a placeholder - in a real scenario you'd have a proper icon
                f.write(b'')
            print(f"   Created placeholder: {icon_source}")
    
    # Copy to target format if needed
    if icon_source.exists() and not icon_target.exists() and icon_source != icon_target:
        shutil.copy2(icon_source, icon_target)
    
    return icon_target if icon_target.exists() else None


def collect_binaries() -> List[tuple]:
    """Collect binary files that need to be included in the build"""
    binaries = []

    # Add Qt platform plugins and minimal X11 libraries for Linux
    if platform.system() == 'Linux':
        # Qt platform plugins - must be in 'platforms/' directory for Qt to find them
        qt_platform_plugins = [
            ('/usr/lib/x86_64-linux-gnu/qt6/plugins/platforms/libqxcb.so', 'platforms/'),
            ('/usr/lib/x86_64-linux-gnu/qt6/plugins/platforms/libqwayland-egl.so', 'platforms/'),
            ('/usr/lib/x86_64-linux-gnu/qt6/plugins/platforms/libqwayland-generic.so', 'platforms/'),
            ('/usr/lib/x86_64-linux-gnu/qt6/plugins/platforms/libqoffscreen.so', 'platforms/'),
        ]

        # Essential Qt6 libraries that need to be bundled - use venv libraries instead of system ones
        qt6_libraries = [
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6Core.so.6', '.'),
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6Gui.so.6', '.'),
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6Widgets.so.6', '.'),
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6Multimedia.so.6', '.'),
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6MultimediaWidgets.so.6', '.'),
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6WebEngineCore.so.6', '.'),
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6WebEngineWidgets.so.6', '.'),
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6WebChannel.so.6', '.'),
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6Network.so.6', '.'),
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6DBus.so.6', '.'),
            ('venv/lib/python3.13/site-packages/PyQt6/Qt6/lib/libQt6OpenGL.so.6', '.'),
        ]

        # Minimal X11 libraries - include more to ensure compatibility
        essential_x11_libraries = [
            ('/lib/x86_64-linux-gnu/libxcb.so.1', '.'),  # Core xcb library
            ('/lib/x86_64-linux-gnu/libX11.so.6', '.'),  # Core X11 library
            ('/lib/x86_64-linux-gnu/libX11-xcb.so.1', '.'),  # X11-xcb bridge
            ('/lib/x86_64-linux-gnu/libxcb-shm.so.0', '.'),  # Shared memory
            ('/lib/x86_64-linux-gnu/libxcb-render.so.0', '.'),  # Render extension
            ('/lib/x86_64-linux-gnu/libxcb-image.so.0', '.'),  # Image utilities
            ('/lib/x86_64-linux-gnu/libxcb-keysyms.so.1', '.'),  # Key symbols
            ('/lib/x86_64-linux-gnu/libxcb-randr.so.0', '.'),  # RandR extension
            ('/lib/x86_64-linux-gnu/libxcb-xfixes.so.0', '.'),  # XFixes extension
        ]

        # GStreamer libraries for multimedia support
        gstreamer_libraries = [
            ('/usr/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0', '.'),
            ('/usr/lib/x86_64-linux-gnu/libgstbase-1.0.so.0', '.'),
            ('/usr/lib/x86_64-linux-gnu/libgstvideo-1.0.so.0', '.'),
            ('/usr/lib/x86_64-linux-gnu/libgstaudio-1.0.so.0', '.'),
            ('/usr/lib/x86_64-linux-gnu/libgstpbutils-1.0.so.0', '.'),
        ]

        binaries.extend(qt_platform_plugins)
        binaries.extend(qt6_libraries)
        binaries.extend(essential_x11_libraries)
        binaries.extend(gstreamer_libraries)

        print("   📦 Including Qt6, X11, and GStreamer libraries for self-contained binary")
        print("   💡 This ensures the binary works on systems without Qt6/GStreamer installed")

    return binaries

def generate_spec_file():
    """Generate PyInstaller spec file"""
    project_root = get_project_root()
    platform_config = get_platform_config()

    # Collect files and imports
    data_files = collect_data_files()
    hidden_imports = collect_hidden_imports()
    binaries = collect_binaries()
    icon_file = create_icon_file()

    # Build pathex (additional paths for imports)
    pathex = [str(project_root)] + platform_config.get('additional_paths', [])

    # Verify ffmpeg-python installation
    try:
        import ffmpeg
        print("   ✅ ffmpeg-python is available")
    except ImportError as e:
        print(f"   ⚠️  ffmpeg-python not found: {e}")
        print("   💡 This may cause build issues. Install with: pip install ffmpeg-python")

    spec_content = f'''# -*- mode: python ; coding: utf-8 -*-

block_cipher = None

a = Analysis(
    ['{BUILD_CONFIG['entry_point']}'],
    pathex={pathex!r},
    binaries={binaries!r},
    datas={data_files!r},
    hiddenimports={hidden_imports!r},
    hookspath={['.']!r},
    hooksconfig={{}},
    runtime_hooks=[],  # Disabled runtime hooks to avoid segfaults
    excludes={platform_config.get('exclude_modules', [])!r},
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='{platform_config['executable_name']}',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx={BUILD_CONFIG['upx']},
    upx_exclude=[],
    runtime_tmpdir=None,
    console={BUILD_CONFIG['console']},
    disable_windowed_traceback=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    version='{BUILD_CONFIG['app_version']}',
    description='{BUILD_CONFIG['description']}',
    icon={repr(str(icon_file)) if icon_file else None},
)
'''
    
    # Add macOS app bundle configuration if on macOS
    if platform.system() == 'Darwin':
        spec_content += f'''
app = BUNDLE(
    exe,
    name='{BUILD_CONFIG['app_name']}.app',
    icon={repr(str(icon_file)) if icon_file else None},
    bundle_identifier='net.nexlab.mbetterclient',
    version='{BUILD_CONFIG['app_version']}',
    info_plist={{
        'CFBundleShortVersionString': '{BUILD_CONFIG['app_version']}',
        'CFBundleVersion': '{BUILD_CONFIG['app_version']}',
        'NSHighResolutionCapable': 'True',
        'NSRequiresAquaSystemAppearance': 'False',
    }},
)
'''
    
    spec_file_path = project_root / f"{BUILD_CONFIG['app_name']}.spec"
    with open(spec_file_path, 'w', encoding='utf-8') as f:
        f.write(spec_content)
    
    print(f"📝 Generated spec file: {spec_file_path}")
    return spec_file_path


def check_dependencies():
    """Check if all required dependencies are available"""
    print("🔍 Checking dependencies...")
    
    # Map package names to their import names (required packages)
    required_packages = {
        'PyInstaller': 'PyInstaller',
        'PyQt6': 'PyQt6'
    }

    # Optional packages (warn if missing but don't fail)
    optional_packages = {
        'ffmpeg-python': 'ffmpeg',
        'dbus-python': 'dbus'
    }
    missing_packages = []
    
    # Check required packages
    for package_name, import_name in required_packages.items():
        try:
            __import__(import_name)
            print(f"   ✓ {package_name}")
        except ImportError:
            missing_packages.append(package_name)
            print(f"   ✗ {package_name}")

    # Check optional packages (warn but don't fail)
    for package_name, import_name in optional_packages.items():
        try:
            __import__(import_name)
            print(f"   ✓ {package_name} (optional)")
        except ImportError:
            print(f"   ⚠️  {package_name} (optional) - not available")

    if missing_packages:
        print(f"\n❌ Missing required dependencies: {', '.join(missing_packages)}")
        print("Please install them using:")
        print(f"   pip install {' '.join(missing_packages)}")
        return False

    return True


def run_pyinstaller(spec_file_path: Path):
    """Run PyInstaller with the generated spec file"""
    print("🔨 Running PyInstaller...")
    
    cmd = [
        sys.executable, '-m', 'PyInstaller',
        '--clean' if BUILD_CONFIG['clean_build'] else '--noconfirm',
        str(spec_file_path)
    ]
    
    print(f"Command: {' '.join(cmd)}")
    
    try:
        result = subprocess.run(cmd, check=True, capture_output=False)
        print("✅ Build completed successfully!")
        return True
    except subprocess.CalledProcessError as e:
        print(f"❌ Build failed with exit code {e.returncode}")
        return False


def post_build_tasks():
    """Perform post-build tasks"""
    print("📦 Performing post-build tasks...")

    dist_dir = get_dist_dir()
    platform_config = get_platform_config()

    if not dist_dir.exists():
        print("   No dist directory found")
        return

    # Removed Qt platform plugins and qt.conf copying for self-contained binary
    print("   📦 Self-contained binary - no external Qt files needed")

    # Find the created executable
    executable_path = None
    for item in dist_dir.iterdir():
        if item.is_file() and item.name == platform_config['executable_name']:
            executable_path = item
            break
        elif item.is_dir() and item.name.endswith('.app'):  # macOS app bundle
            executable_path = item
            break

    if executable_path:
        size_mb = get_file_size_mb(executable_path)
        print(f"   📱 Created: {executable_path} ({size_mb:.1f} MB)")

        # Make executable on Unix systems
        if platform.system() in ['Linux', 'Darwin'] and executable_path.is_file():
            os.chmod(executable_path, 0o755)
            print("   🔧 Made executable")

    # Create distribution package
    create_distribution_package(dist_dir)


def get_file_size_mb(path: Path) -> float:
    """Get file or directory size in MB"""
    if path.is_file():
        return path.stat().st_size / (1024 * 1024)
    elif path.is_dir():
        total_size = sum(f.stat().st_size for f in path.rglob('*') if f.is_file())
        return total_size / (1024 * 1024)
    return 0.0


def create_distribution_package(dist_dir: Path):
    """Create a distribution package"""
    print("📦 Creating distribution package...")
    
    project_root = get_project_root()
    platform_name = platform.system().lower()
    arch = platform.machine().lower()
    
    package_name = f"{BUILD_CONFIG['app_name']}-{BUILD_CONFIG['app_version']}-{platform_name}-{arch}"
    package_dir = project_root / 'packages'
    package_dir.mkdir(exist_ok=True)
    
    # Copy executable and create package
    if platform.system() == 'Windows':
        # Create ZIP package for Windows
        package_file = package_dir / f"{package_name}.zip"
        shutil.make_archive(str(package_file.with_suffix('')), 'zip', str(dist_dir))
        print(f"   📦 Created: {package_file}")
    else:
        # Create tar.gz package for Unix systems
        package_file = package_dir / f"{package_name}.tar.gz"
        shutil.make_archive(str(package_file.with_suffix('').with_suffix('')), 'gztar', str(dist_dir))
        print(f"   📦 Created: {package_file}")
    
    # Create README for distribution
    readme_content = f"""# {BUILD_CONFIG['app_name']} v{BUILD_CONFIG['app_version']}

{BUILD_CONFIG['description']}

## Installation

1. Extract this package to your desired location
2. Run the executable file
3. The application will create necessary configuration files on first run

## System Requirements

- **Operating System**: {platform.system()} {platform.release()}
- **Architecture**: {platform.machine()}
- **Memory**: 512 MB RAM minimum, 1 GB recommended
- **Disk Space**: 100 MB free space

## Configuration

The application stores its configuration and database in:
- **Windows**: `%APPDATA%\\{BUILD_CONFIG['app_name']}`
- **macOS**: `~/Library/Application Support/{BUILD_CONFIG['app_name']}`
- **Linux**: `~/.config/{BUILD_CONFIG['app_name']}`

## Web Interface

By default, the web interface is available at: http://localhost:5001

Default login credentials:
- Username: admin
- Password: admin

**Please change the default password after first login.**

## Support

For support and documentation, please visit: https://git.nexlab.net/mbetter/mbetterc

## Version Information

- Version: {BUILD_CONFIG['app_version']}
- Build Date: {platform.node()}
- Platform: {platform.platform()}
"""
    
    readme_file = package_dir / f"{package_name}-README.txt"
    with open(readme_file, 'w', encoding='utf-8') as f:
        f.write(readme_content)
    
    print(f"   📄 Created: {readme_file}")


def main():
    """Main build process"""
    print(f"🚀 Building {BUILD_CONFIG['app_name']} v{BUILD_CONFIG['app_version']}")
    print(f"Platform: {platform.system()} {platform.release()} ({platform.machine()})")
    print()
    
    # Check dependencies
    if not check_dependencies():
        sys.exit(1)
    
    # Clean build directories
    if BUILD_CONFIG['clean_build']:
        clean_build_directories()
    
    # Generate spec file
    spec_file_path = generate_spec_file()
    
    # Run PyInstaller
    if not run_pyinstaller(spec_file_path):
        sys.exit(1)
    
    # Post-build tasks
    post_build_tasks()
    
    print()
    print("🎉 Build process completed successfully!")
    print(f"📁 Distribution files are in: {get_dist_dir()}")
    print(f"📦 Packages are in: {get_project_root() / 'packages'}")


if __name__ == '__main__':
    main()