"""Container diff functionality.

Compare container filesystems and detect changes between:
- Two containers
- Container and its snapshots
- Container and base image (Docker)
- Two snapshots

Example:
    >>> from gaol.diff import compare_containers, compare_snapshots
    >>>
    >>> # Compare two containers
    >>> result = compare_containers("container-a", "container-b", runtime="nspawn")
    >>> print(f"Files changed: {result.summary.total_file_changes}")
    >>>
    >>> # Compare snapshots
    >>> result = compare_snapshots("mycontainer", "snap1", "snap2", runtime="docker")
    >>> for change in result.file_changes:
    ...     print(f"{change.change_type}: {change.path}")
"""

from gaol.diff.engine import DiffEngine
from gaol.diff.exceptions import (
    DiffContainerNotRunningError,
    DiffError,
    DiffFilesystemAccessError,
    DiffImageNotFoundError,
    DiffPackageDetectionError,
    DiffSnapshotNotFoundError,
    DiffSourceNotFoundError,
    DiffTargetNotFoundError,
    DiffUnsupportedRuntimeError,
)
from gaol.diff.formatter import (
    ConsoleFormatter,
    DiffFormatter,
    JsonFormatter,
    get_formatter,
)
from gaol.diff.models import (
    DiffOptions,
    DiffResult,
    DiffSummary,
    FileChange,
    FileChangeType,
    FileEntry,
    FilesystemManifest,
    FileType,
    PackageChange,
    PackageDiff,
    PackageInfo,
)
from gaol.diff.package import PackageDetector
from gaol.diff.readers import FilesystemReader, get_reader

__all__ = [
    # Models
    "DiffOptions",
    "DiffResult",
    "DiffSummary",
    "FileChange",
    "FileChangeType",
    "FileEntry",
    "FilesystemManifest",
    "FileType",
    "PackageChange",
    "PackageDiff",
    "PackageInfo",
    # Engine
    "DiffEngine",
    # Readers
    "FilesystemReader",
    "get_reader",
    # Package detection
    "PackageDetector",
    # Formatters
    "DiffFormatter",
    "ConsoleFormatter",
    "JsonFormatter",
    "get_formatter",
    # Exceptions
    "DiffError",
    "DiffSourceNotFoundError",
    "DiffTargetNotFoundError",
    "DiffSnapshotNotFoundError",
    "DiffImageNotFoundError",
    "DiffFilesystemAccessError",
    "DiffPackageDetectionError",
    "DiffContainerNotRunningError",
    "DiffUnsupportedRuntimeError",
    # High-level API
    "compare_containers",
    "compare_snapshots",
    "compare_to_image",
    "audit_container",
]


def compare_containers(
    source_id: str,
    target_id: str,
    runtime: str = "nspawn",
    options: DiffOptions | None = None,
    include_packages: bool = True,
) -> DiffResult:
    """Compare two container filesystems.

    Args:
        source_id: Source container name/ID
        target_id: Target container name/ID
        runtime: Container runtime (nspawn, docker)
        options: Diff options
        include_packages: Whether to compare installed packages

    Returns:
        DiffResult with all changes
    """
    if options is None:
        options = DiffOptions()

    reader = get_reader(runtime)
    engine = DiffEngine()

    # Read both containers
    source_manifest = reader.read_container(source_id, options)
    target_manifest = reader.read_container(target_id, options)

    # Compute diff
    result = engine.compute_diff(source_manifest, target_manifest, options)

    # Detect packages if requested
    if include_packages and options.compare_packages:
        detector = PackageDetector(reader)

        source_packages = detector.detect_packages(source_id)
        target_packages = detector.detect_packages(target_id)

        if source_packages or target_packages:
            result.package_diff = engine.compute_package_diff(
                source_packages, target_packages
            )
            engine.update_summary_with_packages(result.summary, result.package_diff)

    return result


def compare_snapshots(
    container_name: str,
    source_snapshot: str,
    target_snapshot: str,
    runtime: str = "nspawn",
    options: DiffOptions | None = None,
    include_packages: bool = True,
) -> DiffResult:
    """Compare two snapshots of a container.

    Args:
        container_name: Container name
        source_snapshot: Source snapshot name
        target_snapshot: Target snapshot name
        runtime: Container runtime (nspawn, docker)
        options: Diff options
        include_packages: Whether to compare installed packages

    Returns:
        DiffResult with all changes
    """
    if options is None:
        options = DiffOptions()

    reader = get_reader(runtime)
    engine = DiffEngine()

    # Read both snapshots
    source_manifest = reader.read_snapshot(container_name, source_snapshot, options)
    target_manifest = reader.read_snapshot(container_name, target_snapshot, options)

    # Compute diff
    result = engine.compute_diff(source_manifest, target_manifest, options)

    # Package detection for snapshots is more complex
    # Skip for now as it requires runtime-specific handling

    return result


def compare_to_image(
    container_id: str,
    image_name: str | None = None,
    runtime: str = "docker",
    options: DiffOptions | None = None,
    include_packages: bool = True,
) -> DiffResult:
    """Compare container to its base image.

    Docker only - compares a container's current state to its base image.

    Args:
        container_id: Container name/ID
        image_name: Base image name (auto-detected if None)
        runtime: Must be "docker"
        options: Diff options
        include_packages: Whether to compare installed packages

    Returns:
        DiffResult with all changes from base image
    """
    if runtime != "docker":
        raise DiffUnsupportedRuntimeError(
            runtime, "Image comparison only supported for Docker"
        )

    if options is None:
        options = DiffOptions()

    reader = get_reader(runtime)
    engine = DiffEngine()

    # Read container
    container_manifest = reader.read_container(container_id, options)

    # Get image name if not provided
    if image_name is None:
        # TODO: Auto-detect from container labels
        raise ValueError("image_name required (auto-detection not yet implemented)")

    # Read base image
    image_manifest = reader.read_image(image_name, options)

    # Compute diff (image is source, container is target)
    result = engine.compute_diff(image_manifest, container_manifest, options)

    # Detect packages if requested
    if include_packages and options.compare_packages:
        detector = PackageDetector(reader)
        container_packages = detector.detect_packages(container_id)

        # For images, we'd need to read packages differently
        # Skip for now - would need to create temp container from image

    return result


def audit_container(
    container_name: str,
    runtime: str = "nspawn",
    options: DiffOptions | None = None,
    include_packages: bool = True,
) -> DiffResult:
    """Audit a container against its most recent snapshot.

    Compares the current container state to the most recent snapshot,
    showing all changes made since that snapshot was taken.

    Args:
        container_name: Container name
        runtime: Container runtime (nspawn, docker)
        options: Diff options
        include_packages: Whether to compare installed packages

    Returns:
        DiffResult with changes since last snapshot
    """
    from gaol.runtimes import get_runtime

    if options is None:
        options = DiffOptions()

    # Get most recent snapshot
    runtime_obj = get_runtime(runtime)
    snapshots = runtime_obj.snapshot_list(container_name)

    if not snapshots:
        raise DiffSnapshotNotFoundError(
            container_name, "No snapshots found for auditing"
        )

    # Snapshots should be sorted by date, most recent first
    latest_snapshot = snapshots[0].name

    reader = get_reader(runtime)
    engine = DiffEngine()

    # Read snapshot (source/baseline) and current container (target)
    source_manifest = reader.read_snapshot(container_name, latest_snapshot, options)
    target_manifest = reader.read_container(container_name, options)

    # Compute diff
    result = engine.compute_diff(source_manifest, target_manifest, options)

    # Detect packages if requested
    if include_packages and options.compare_packages:
        detector = PackageDetector(reader)
        current_packages = detector.detect_packages(container_name)

        # For snapshots, package detection is handled differently
        # We can try to read from the snapshot filesystem
        snapshot_packages = detector.detect_packages_from_snapshot(
            container_name, latest_snapshot
        )

        if current_packages or snapshot_packages:
            result.package_diff = engine.compute_package_diff(
                snapshot_packages, current_packages
            )
            engine.update_summary_with_packages(result.summary, result.package_diff)

    return result
