"""Tests for TrackManifest and TrackManifestCollection domain objects."""

from __future__ import annotations

import dataclasses

from scanner.manifest import TrackManifest, TrackManifestCollection


def _make_manifest(
    relative_path: str = "song.wav",
    sha256: str = "abc123",
    file_size: int = 1024,
) -> TrackManifest:
    """Create a TrackManifest with sensible defaults for testing.

    Args:
        relative_path: Relative path for the manifest.
        sha256: SHA-256 hash for the manifest.
        file_size: File size in bytes.

    Returns:
        A :class:`TrackManifest` instance.
    """
    return TrackManifest(
        relative_path=relative_path,
        original_filename=relative_path.split("/")[-1],
        file_size=file_size,
        sha256=sha256,
    )


def test_manifest_is_frozen() -> None:
    """Verify that TrackManifest is immutable."""
    manifest = _make_manifest()
    try:
        manifest.relative_path = "other.wav"
        raise AssertionError("Should have raised FrozenInstanceError")
    except dataclasses.FrozenInstanceError:
        pass


def test_manifest_fields() -> None:
    """Verify that TrackManifest stores all fields correctly."""
    manifest = TrackManifest(
        relative_path="album/track.flac",
        original_filename="track.flac",
        file_size=2048,
        sha256="deadbeef",
    )
    assert manifest.relative_path == "album/track.flac"
    assert manifest.original_filename == "track.flac"
    assert manifest.file_size == 2048
    assert manifest.sha256 == "deadbeef"


def test_collection_empty() -> None:
    """Verify that an empty collection has zero length."""
    collection = TrackManifestCollection()
    assert len(collection) == 0
    assert collection.paths == set()


def test_collection_find_by_path() -> None:
    """Verify that find_by_path returns the correct manifest."""
    m1 = _make_manifest("a.wav", "hash1")
    m2 = _make_manifest("b.wav", "hash2")
    collection = TrackManifestCollection([m1, m2])

    found = collection.find_by_path("a.wav")
    assert found is m1

    not_found = collection.find_by_path("c.wav")
    assert not_found is None


def test_collection_find_by_sha256() -> None:
    """Verify that find_by_sha256 returns all matching manifests."""
    m1 = _make_manifest("a.wav", "shared_hash")
    m2 = _make_manifest("b.wav", "shared_hash")
    m3 = _make_manifest("c.wav", "unique_hash")
    collection = TrackManifestCollection([m1, m2, m3])

    matches = collection.find_by_sha256("shared_hash")
    assert len(matches) == 2
    assert m1 in matches
    assert m2 in matches

    unique = collection.find_by_sha256("unique_hash")
    assert len(unique) == 1
    assert unique[0] is m3


def test_collection_get_duplicates() -> None:
    """Verify that get_duplicates returns only hashes with multiple manifests."""
    m1 = _make_manifest("a.wav", "dup_hash")
    m2 = _make_manifest("b.wav", "dup_hash")
    m3 = _make_manifest("c.wav", "unique_hash")
    collection = TrackManifestCollection([m1, m2, m3])

    duplicates = collection.get_duplicates()
    assert "dup_hash" in duplicates
    assert len(duplicates["dup_hash"]) == 2
    assert "unique_hash" not in duplicates


def test_collection_paths_property() -> None:
    """Verify that paths property returns all relative paths."""
    m1 = _make_manifest("x.wav", "h1")
    m2 = _make_manifest("y.wav", "h2")
    collection = TrackManifestCollection([m1, m2])

    assert collection.paths == {"x.wav", "y.wav"}


def test_collection_iteration() -> None:
    """Verify that collection supports iteration."""
    m1 = _make_manifest("a.wav", "h1")
    m2 = _make_manifest("b.wav", "h2")
    collection = TrackManifestCollection([m1, m2])

    items = list(collection)
    assert len(items) == 2
    assert m1 in items
    assert m2 in items
