"""
Asset management CLI commands for MarkiTect - Issue #143.

This module implements CLI commands for asset management including:
- Asset management: add, list, stats, cleanup
- Package management: create, extract, list, validate
- Workspace management: init, status, sync

Commands integrate with AssetManager backend from Issue #142 and use
common CLI utilities for consistent user experience.
"""

import click
import sys
from pathlib import Path

# Import asset management backend
try:
    from .assets import AssetManager
    ASSET_BACKEND_AVAILABLE = True
except ImportError:
    ASSET_BACKEND_AVAILABLE = False

# Import CLI utilities
from .cli_utils import (
    ClickOutputFormatter, handle_asset_errors,
    output_format_option, dry_run_option, get_asset_config,
    validate_file_path, validate_directory_path
)


def get_asset_manager() -> 'AssetManager':
    """
    Get configured AssetManager instance with current configuration.

    Returns:
        AssetManager: Configured instance ready for asset operations

    Raises:
        SystemExit: If asset management backend is not available
    """
    if not ASSET_BACKEND_AVAILABLE:
        ClickOutputFormatter.error("Asset management backend not available")

    # Get configuration with defaults
    config = get_asset_config()
    return AssetManager(config={'assets': config})


# Asset management command group
@click.group()
def asset():
    """
    Asset management commands for MarkiTect.

    Manage assets with content-addressable storage, deduplication, and
    cross-platform symlink support. Assets are stored in a shared location
    and can be referenced from multiple markdown documents.

    \b
    Examples:
        markitect asset add logo.png ./project --name company_logo.png
        markitect asset list --format json
        markitect asset stats
        markitect asset cleanup --dry-run
    """
    pass


@asset.command('add')
@click.argument('file_path', type=click.Path(exists=True))
@click.argument('document_path', type=click.Path())
@click.option('--name', help='Virtual name in document (default: original filename)')
@click.option('--force', is_flag=True, help='Overwrite existing virtual name')
@click.option('--no-symlink', is_flag=True, help='Force file copy instead of symlink')
@handle_asset_errors
def asset_add(file_path, document_path, name, force, no_symlink):
    """
    Add asset to the shared asset library with automatic deduplication.

    Adds the specified file to the asset management system, automatically
    deduplicating if the same content already exists. Assets are stored
    using content-addressable hashing and can be referenced with virtual
    names in markdown documents.

    \b
    Arguments:
        FILE_PATH     Path to the asset file to add
        DOCUMENT_PATH Path to the document directory where asset will be used

    \b
    Features:
        - Automatic content-based deduplication
        - Cross-platform symlink support with fallback to copying
        - Virtual naming for flexible document organization
        - Hash-based integrity verification
    """
    manager = get_asset_manager()

    # Validate paths
    file_path = validate_file_path(file_path, must_exist=True)
    document_path = validate_directory_path(document_path, must_exist=False, create_if_missing=True)

    # Use original filename if name not specified
    virtual_name = name or file_path.name

    # Add the asset
    result = manager.add_asset(file_path, f"Added to {document_path}")

    # Display results
    details = {
        'Hash': result.get('hash', 'N/A')[:16] + '...' if result.get('hash') else 'N/A',
        'Virtual name': virtual_name,
        'Size': f"{result.get('size', 'N/A')} bytes"
    }

    ClickOutputFormatter.success("Asset added successfully", details)

    if result.get('deduplicated', False):
        ClickOutputFormatter.info("Asset was deduplicated with existing content")


@asset.command('list')
@click.option('--document', type=click.Path(), help='Filter by document directory')
@click.option('--unused', is_flag=True, help='Show only unused assets')
@output_format_option()
@click.option('--sort', 'sort_field', type=click.Choice(['name', 'size', 'date']), default='name',
              help='Sort by field (default: name)')
@handle_asset_errors
def asset_list(document, unused, output_format, sort_field):
    """List assets."""
    manager = get_asset_manager()
    assets = manager.list_assets()

    if not assets:
        ClickOutputFormatter.info("No assets found")
        return

    if output_format == 'json':
        ClickOutputFormatter.json_output(assets)
    else:
        # Prepare table data
        table_data = []
        for asset in assets:
            table_data.append({
                'Hash': asset.get('hash', 'N/A')[:12],  # Short hash
                'Description': asset.get('description', 'N/A'),
                'Size': asset.get('size', 0),
                'Date': asset.get('created_at', 'N/A')
            })

        headers = ['Hash', 'Description', 'Size', 'Date']
        ClickOutputFormatter.table(table_data, headers)


@asset.command('stats')
@handle_asset_errors
def asset_stats():
    """Show asset library statistics."""
    manager = get_asset_manager()
    stats = manager.get_storage_stats()

    ClickOutputFormatter.info("Asset Library Statistics")
    details = {
        'Total assets': stats.get('total_assets', 0),
        'Storage size': f"{stats.get('total_size', 0)} bytes",
        'Deduplication savings': f"{stats.get('dedupe_savings', 0)} bytes"
    }

    if stats.get('total_size', 0) > 0:
        savings_pct = (stats.get('dedupe_savings', 0) / stats.get('total_size', 1)) * 100
        details['Space saved'] = f"{savings_pct:.1f}%"

    ClickOutputFormatter.info("", details)


@asset.command('cleanup')
@click.option('--orphaned', is_flag=True, help='Clean only orphaned assets')
@dry_run_option()
@handle_asset_errors
def asset_cleanup(orphaned, dry_run):
    """Clean unused assets."""
    manager = get_asset_manager()

    if dry_run:
        ClickOutputFormatter.info("DRY RUN - no files will be removed")

    # Get cleanup info
    result = manager.cleanup_orphaned_assets()
    removed_count = result.get('removed_count', 0)
    freed_bytes = result.get('freed_bytes', 0)

    if dry_run:
        ClickOutputFormatter.info(f"Would remove {removed_count} orphaned assets")
        if freed_bytes > 0:
            ClickOutputFormatter.info(f"Would free {freed_bytes} bytes")
    else:
        if removed_count > 0:
            details = {
                'Removed assets': removed_count,
                'Freed space': f"{freed_bytes} bytes"
            }
            ClickOutputFormatter.success("Cleanup completed", details)
        else:
            ClickOutputFormatter.info("No orphaned assets found")


# Package management command group
@click.group()
def package():
    """
    Package management commands for MarkiTect.

    Create, extract, validate, and manage .mdpkg packages containing
    markdown documents and their associated assets. Packages use ZIP
    format with manifest metadata for reliable distribution.

    \b
    Examples:
        markitect package create ./project project_v1
        markitect package extract project_v1.mdpkg --name new_project
        markitect package list --format table
        markitect package validate project_v1.mdpkg
    """
    pass


@package.command('create')
@click.argument('document_dir', type=click.Path(exists=True))
@click.argument('package_name')
@click.option('--output', type=click.Path(), help='Output directory (default: workspace/packages)')
@click.option('--compression', type=int, default=6, help='ZIP compression level 0-9 (default: 6)')
@click.option('--exclude', multiple=True, help='Exclude files matching pattern')
@click.option('--include-sources', is_flag=True, help='Include source markdown files')
@click.option('--validate', is_flag=True, help='Validate package after creation')
@handle_asset_errors
def package_create(document_dir, package_name, output, compression, exclude, include_sources, validate):
    """
    Create a .mdpkg package from a document directory.

    Packages a directory containing markdown documents and assets into
    a distributable .mdpkg file (ZIP format). Includes manifest metadata
    for reliable extraction and validation.

    \b
    Arguments:
        DOCUMENT_DIR  Directory containing markdown documents and assets
        PACKAGE_NAME  Name for the package (without .mdpkg extension)

    \b
    Features:
        - ZIP-based packaging with configurable compression
        - Manifest metadata for validation and extraction
        - Asset embedding and path rewriting
        - Exclusion patterns for selective packaging
    """
    manager = get_asset_manager()

    # Validate and prepare paths
    document_dir = validate_directory_path(document_dir, must_exist=True)

    # Determine output path
    if output:
        output_dir = validate_directory_path(output, must_exist=False, create_if_missing=True)
    else:
        output_dir = validate_directory_path("packages", must_exist=False, create_if_missing=True)

    package_path = output_dir / f"{package_name}.mdpkg"

    # Create package using AssetManager
    result = manager.create_package(document_dir, package_path)

    # Display results
    details = {
        'Package': str(package_path),
        'Files': result.get('files_count', 0),
        'Size': f"{result.get('total_size', 0)} bytes"
    }

    ClickOutputFormatter.success("Package created successfully", details)

    if validate:
        # Basic validation - check if file exists and is readable
        if package_path.exists():
            ClickOutputFormatter.success("Package validation passed")
        else:
            ClickOutputFormatter.error("Package validation failed")


@package.command('extract')
@click.argument('package_file', type=click.Path(exists=True))
@click.option('--name', help='Custom extraction name')
def package_extract(package_file, name):
    """Extract package."""
    try:
        manager = get_asset_manager()
        package_path = Path(package_file)

        # Determine extraction directory
        if name:
            extract_dir = Path.cwd() / name
        else:
            extract_dir = Path.cwd() / package_path.stem

        # Extract package using AssetManager
        result = manager.extract_package(package_path, extract_dir)

        click.echo("Package extracted successfully!")
        click.echo(f"Extracted to: {extract_dir}")
        click.echo(f"Files: {result.get('files_count', 0)}")

    except PackagingError as e:
        click.echo(f"Error extracting package: {e}", err=True)
        sys.exit(1)
    except Exception as e:
        click.echo(f"Unexpected error: {e}", err=True)
        sys.exit(1)


@package.command('list')
@output_format_option()
@handle_asset_errors
def package_list(output_format):
    """List packages."""
    # Find .mdpkg files in common locations
    package_dirs = [Path.cwd() / "packages", Path.cwd()]
    packages = []

    for pkg_dir in package_dirs:
        if pkg_dir.exists():
            for pkg_file in pkg_dir.glob("*.mdpkg"):
                packages.append({
                    'Name': pkg_file.name,
                    'Size': pkg_file.stat().st_size
                })

    if not packages:
        ClickOutputFormatter.info("No packages found")
        return

    if output_format == 'json':
        ClickOutputFormatter.json_output(packages)
    else:
        headers = ['Name', 'Size']
        ClickOutputFormatter.table(packages, headers)


@package.command('validate')
@click.argument('package_file', type=click.Path(exists=True))
def package_validate(package_file):
    """Validate package integrity."""
    try:
        package_path = Path(package_file)

        # Basic validation
        if not package_path.suffix == '.mdpkg':
            click.echo("Invalid package: must have .mdpkg extension", err=True)
            sys.exit(1)

        if package_path.stat().st_size == 0:
            click.echo("Invalid package: file is empty", err=True)
            sys.exit(1)

        # Try to read as ZIP
        import zipfile
        try:
            with zipfile.ZipFile(package_path, 'r') as zf:
                # Check for manifest
                if 'manifest.json' not in zf.namelist():
                    click.echo("Warning: Package missing manifest.json")

            click.echo("Package is valid")

        except zipfile.BadZipFile:
            click.echo("Invalid package: not a valid ZIP file", err=True)
            sys.exit(1)

    except Exception as e:
        click.echo(f"Error validating package: {e}", err=True)
        sys.exit(1)


# Workspace management command group
@click.group()
def workspace():
    """
    Workspace management commands for MarkiTect.

    Initialize, manage, and synchronize MarkiTect workspaces containing
    shared assets, packages, and configuration. Workspaces provide a
    structured environment for markdown document management.

    \b
    Examples:
        markitect workspace init --template basic
        markitect workspace status
        markitect workspace sync --document ./project
    """
    pass


@workspace.command('init')
@click.option('--template', help='Workspace template to use')
@handle_asset_errors
def workspace_init(template):
    """Initialize workspace."""
    workspace_dir = Path.cwd() / "markitect_workspace"

    if workspace_dir.exists():
        ClickOutputFormatter.info(f"Workspace already exists at: {workspace_dir}")
        return

    # Create workspace structure
    workspace_dir.mkdir(parents=True, exist_ok=True)
    (workspace_dir / "shared_assets").mkdir(exist_ok=True)
    (workspace_dir / "packages").mkdir(exist_ok=True)

    # Create basic config file if using template
    if template:
        ClickOutputFormatter.info(f"Using template: {template}")

    details = {'Location': str(workspace_dir)}
    ClickOutputFormatter.success("Workspace initialized successfully", details)


@workspace.command('status')
def workspace_status():
    """Show workspace status."""
    try:
        workspace_dir = Path.cwd() / "markitect_workspace"

        if not workspace_dir.exists():
            click.echo("No workspace found in current directory")
            click.echo("Run 'markitect workspace init' to create one")
            return

        click.echo("Workspace Status")
        click.echo("=" * 16)
        click.echo(f"Location: {workspace_dir}")

        # Count assets and packages
        assets_dir = workspace_dir / "shared_assets"
        packages_dir = workspace_dir / "packages"

        if assets_dir.exists():
            asset_count = len(list(assets_dir.iterdir()))
            click.echo(f"Assets: {asset_count}")

        if packages_dir.exists():
            package_count = len(list(packages_dir.glob("*.mdpkg")))
            click.echo(f"Packages: {package_count}")

    except Exception as e:
        click.echo(f"Error getting workspace status: {e}", err=True)
        sys.exit(1)


@workspace.command('sync')
@click.option('--document', type=click.Path(), help='Sync specific document')
def workspace_sync(document):
    """Sync workspace assets."""
    try:
        workspace_dir = Path.cwd() / "markitect_workspace"

        if not workspace_dir.exists():
            click.echo("No workspace found. Run 'markitect workspace init' first.", err=True)
            sys.exit(1)

        if document:
            click.echo(f"Synchronizing document: {document}")
        else:
            click.echo("Synchronizing entire workspace")

        # Basic sync - ensure directories exist
        (workspace_dir / "shared_assets").mkdir(exist_ok=True)
        (workspace_dir / "packages").mkdir(exist_ok=True)

        click.echo("Workspace synchronized")

    except Exception as e:
        click.echo(f"Error syncing workspace: {e}", err=True)
        sys.exit(1)