"""Operations registry."""

from collections.abc import Callable
from pathlib import Path

from cascade_fm.core.filetypes import detect_mime_type
from cascade_fm.operations.base import Operation
from cascade_fm.operations.create_archive import CreateArchive
from cascade_fm.operations.filter import FilterExtension, FilterName
from cascade_fm.operations.image_transform import ImageTransform
from cascade_fm.operations.images import (
    CompressImage,
    ConvertImage,
    ResizeImage,
    RotateImage,
    StripExif,
)
from cascade_fm.operations.rename import RenamePattern
from cascade_fm.operations.save_to import SaveTo
from cascade_fm.operations.unarchive import Unarchive

# Operation factory functions
OPERATION_FACTORIES = {
    "filter_extension": FilterExtension,
    "filter_name": FilterName,
    "image_transform": ImageTransform,
    "create_archive": CreateArchive,
    "unarchive": Unarchive,
    "rename_pattern": RenamePattern,
    "resize_image": ResizeImage,
    "convert_image": ConvertImage,
    "rotate_image": RotateImage,
    "strip_exif": StripExif,
    "compress_image": CompressImage,
    "save_to": SaveTo,
}

UI_HIDDEN_OPERATIONS = {
    "filter_extension",
    "filter_name",
    "resize_image",
    "convert_image",
    "rotate_image",
    "strip_exif",
    "compress_image",
}

UI_SELECTION_DEPENDENT_OPERATIONS = {
    "image_transform",
    "unarchive",
}


def create_operation(
    operation_name: str,
    on_output_changed: Callable[[list[Path]], None] | None = None,
) -> Operation:
    """Create an operation instance.

    Args:
        operation_name: Name of the operation to create.
        on_output_changed: Callback for output changes.

    Returns:
        New operation instance.

    Raises:
        KeyError: If operation_name is not registered.
    """
    factory = OPERATION_FACTORIES[operation_name]
    return factory(on_output_changed=on_output_changed)


def get_operation_info(operation_name: str) -> dict[str, str]:
    """Get operation metadata without creating an instance.

    Args:
        operation_name: Name of the operation.

    Returns:
        Dict with 'name', 'label', 'description' keys.
    """
    # Create temporary instance to get metadata
    op = create_operation(operation_name)
    return {
        "name": op.name,
        "label": op.label,
        "description": op.description,
    }


def get_operation_accepts(operation_name: str) -> list[str]:
    """Get accepted MIME patterns for an operation."""
    op = create_operation(operation_name)
    return op.accepts


def get_operation_group_label(operation_name: str) -> str:
    """Get human-readable MIME-type group label for an operation."""
    accepts = get_operation_accepts(operation_name)

    if not accepts:
        return "Other"

    if accepts == ["*/*"]:
        return "Any Files"

    if len(accepts) == 1:
        pattern = accepts[0]
        if pattern.endswith("/*"):
            major = pattern.split("/", maxsplit=1)[0]
            return f"{major.title()} Files"
        return pattern

    return "Mixed Types"


def list_operations() -> list[str]:
    """List all available operation names.

    Returns:
        List of operation names.
    """
    return list(OPERATION_FACTORIES.keys())


def list_operations_for_ui(files: list[Path] | None = None) -> list[str]:
    """List operations shown in the UI.

    Legacy granular operations remain registered for compatibility/tests,
    but are hidden from operation pickers.
    """
    operations = list_operations_for_files(files) if files is not None else list_operations()
    has_selection = bool(files)
    return [
        operation_name
        for operation_name in operations
        if operation_name not in UI_HIDDEN_OPERATIONS
        and (has_selection or operation_name not in UI_SELECTION_DEPENDENT_OPERATIONS)
    ]


def _mime_matches_pattern(mime_type: str, pattern: str) -> bool:
    """Check whether a MIME type matches an accepts pattern."""
    if pattern == "*/*":
        return True

    if pattern.endswith("/*"):
        major = pattern.split("/", maxsplit=1)[0]
        return mime_type.startswith(f"{major}/")

    return mime_type == pattern


def operation_accepts_files(operation_name: str, files: list[Path]) -> bool:
    """Check whether operation accepts all provided files by MIME type."""
    if not files:
        return True

    operation = create_operation(operation_name)
    patterns = operation.accepts
    for file_path in files:
        mime_type = detect_mime_type(file_path)
        if not any(_mime_matches_pattern(mime_type, pattern) for pattern in patterns):
            return False

    return True


def list_operations_for_files(files: list[Path]) -> list[str]:
    """List operations compatible with all provided files."""
    return [
        operation_name
        for operation_name in list_operations()
        if operation_accepts_files(operation_name, files)
    ]


def list_hidden_operations_for_files(files: list[Path]) -> list[str]:
    """List operations incompatible with the provided files."""
    compatible = set(list_operations_for_files(files))
    return [
        operation_name for operation_name in list_operations() if operation_name not in compatible
    ]
