"""
Bino Build Command

Run rust_bundler build to emit client/server bundles and manifest.
"""

import subprocess
import logging
import json
from pathlib import Path
from typing import Optional, Dict, Any
import shutil

logger = logging.getLogger(__name__)


def build_project(output_dir: Path, production: bool = True) -> None:
    """
    Build the Bino project for production deployment.
    
    Args:
        output_dir: Directory where build output will be placed
        production: Whether to build in production mode
        
    Raises:
        subprocess.CalledProcessError: If build process fails
        FileNotFoundError: If required source files are missing
    """
    project_dir = Path.cwd()
    
    logger.info(f"Building project in {project_dir}")
    logger.info(f"Output directory: {output_dir}")
    
    # Validate project structure
    _validate_project_structure(project_dir)
    
    # Clean output directory
    if output_dir.exists():
        shutil.rmtree(output_dir)
    output_dir.mkdir(parents=True)
    
    # Build client and server bundles
    manifest = _build_bundles(project_dir, output_dir, production)
    
    # Copy static assets
    _copy_static_assets(project_dir, output_dir)
    
    # Generate build manifest
    _generate_build_manifest(output_dir, manifest)
    
    logger.info("✅ Build completed successfully")


def _validate_project_structure(project_dir: Path) -> None:
    """
    Validate that the project has required structure for building.
    
    Args:
        project_dir: Project directory to validate
        
    Raises:
        FileNotFoundError: If required files/directories are missing
    """
    required_paths = [
        project_dir / "app",
        project_dir / "main.py"
    ]
    
    for path in required_paths:
        if not path.exists():
            raise FileNotFoundError(f"Required path not found: {path}")
    
    # Check for package.json
    if not (project_dir / "package.json").exists():
        logger.warning("package.json not found - Node.js dependencies may be missing")


def _build_bundles(project_dir: Path, output_dir: Path, production: bool) -> Dict[str, Any]:
    """
    Build client and server bundles using Rust bundler.
    
    Args:
        project_dir: Source project directory
        output_dir: Build output directory
        production: Production build flag
        
    Returns:
        Build manifest with asset mappings
    """
    # TODO: implement actual rust bundler invocation
    logger.info("Building client bundle...")
    logger.info("Building server bundle...")
    
    # This would call the rust_bundler binary
    # manifest = build_production(project_dir, output_dir, production)
    
    # Mock manifest for now
    manifest = {
        "client": {
            "entry": "client.js",
            "assets": ["client.js", "client.css"]
        },
        "server": {
            "entry": "server.js",
            "assets": ["server.js"]
        },
        "routes": {
            "/": "page.js",
            "/about": "about.js"
        }
    }
    
    return manifest


def _copy_static_assets(project_dir: Path, output_dir: Path) -> None:
    """
    Copy static assets to build output.
    
    Args:
        project_dir: Source project directory
        output_dir: Build output directory
    """
    static_dir = project_dir / "static"
    if static_dir.exists():
        target_static = output_dir / "static"
        shutil.copytree(static_dir, target_static)
        logger.info("Copied static assets")
    
    # Copy public directory if it exists
    public_dir = project_dir / "public"
    if public_dir.exists():
        for item in public_dir.iterdir():
            if item.is_file():
                shutil.copy2(item, output_dir / item.name)
            elif item.is_dir():
                shutil.copytree(item, output_dir / item.name)
        logger.info("Copied public assets")


def _generate_build_manifest(output_dir: Path, manifest: Dict[str, Any]) -> None:
    """
    Generate build manifest file for production server.
    
    Args:
        output_dir: Build output directory
        manifest: Build manifest data
    """
    manifest_file = output_dir / "manifest.json"
    with manifest_file.open("w") as f:
        json.dump(manifest, f, indent=2)
    
    logger.info(f"Generated build manifest: {manifest_file}")


def clean_build(output_dir: Path) -> None:
    """
    Clean previous build artifacts.
    
    Args:
        output_dir: Directory to clean
    """
    if output_dir.exists():
        shutil.rmtree(output_dir)
        logger.info(f"Cleaned build directory: {output_dir}")


def get_build_info(output_dir: Path) -> Optional[Dict[str, Any]]:
    """
    Get information about the current build.
    
    Args:
        output_dir: Build output directory
        
    Returns:
        Build information or None if no build exists
        
    Example:
        >>> info = get_build_info(Path("dist"))
        >>> if info:
        ...     print(f"Build created: {info['timestamp']}")
    """
    manifest_file = output_dir / "manifest.json"
    if not manifest_file.exists():
        return None
    
    try:
        with manifest_file.open() as f:
            return json.load(f)
    except Exception as e:
        logger.error(f"Failed to read build manifest: {e}")
        return None


if __name__ == "__main__":
    # Example usage
    output_path = Path("dist")
    build_project(output_path, production=True)
    
    build_info = get_build_info(output_path)
    if build_info:
        print(f"Build completed with {len(build_info.get('routes', {}))} routes")

# Unit tests as comments:
# 1. test_build_project_success() - verify complete build process works
# 2. test_validate_project_structure() - test project validation logic
# 3. test_build_manifest_generation() - verify manifest.json is created correctly