from types import SimpleNamespace

from core.wishlist import payloads


def test_sanitize_track_data_for_processing_normalizes_artists_and_album():
    track = {
        "name": "Song",
        "album": 123,
        "artists": [{"name": "Artist One"}, "Artist Two", SimpleNamespace(name="Artist Three")],
    }

    out = payloads.sanitize_track_data_for_processing(track)

    assert out["album"] == "123"
    assert out["artists"] == ["Artist One", "Artist Two", "namespace(name='Artist Three')"]


def test_get_track_artist_name_prefers_artists_list_then_artist_field():
    assert payloads.get_track_artist_name({"artists": [{"name": "Artist One"}]}) == "Artist One"
    assert payloads.get_track_artist_name({"artist": "Solo Artist"}) == "Solo Artist"
    assert payloads.get_track_artist_name({}) == "Unknown Artist"


def test_ensure_spotify_track_format_preserves_existing_shape():
    track = {
        "id": "sp-1",
        "name": "Song",
        "artists": [{"name": "Artist One"}],
        "album": {"name": "Album", "album_type": "ep", "total_tracks": 4},
    }

    out = payloads.ensure_spotify_track_format(track)

    assert out is track


def test_ensure_spotify_track_format_builds_webui_shape():
    track = {
        "name": "Song",
        "artist": "Artist One",
        "album": {"name": "Album One", "release_date": "2024-01-01"},
        "duration_ms": 1234,
        "track_number": 7,
        "disc_number": 2,
        "preview_url": "https://example.test/preview",
        "external_urls": {"spotify": "https://open.spotify.com/track/1"},
        "popularity": 42,
    }

    out = payloads.ensure_spotify_track_format(track)

    assert out["name"] == "Song"
    assert out["artists"] == [{"name": "Artist One"}]
    assert out["album"]["name"] == "Album One"
    assert out["album"]["album_type"] == "album"
    assert out["album"]["total_tracks"] == 0
    assert out["source"] == "webui_modal"


def test_ensure_wishlist_track_format_aliases_the_spotify_helper():
    track = {
        "name": "Song",
        "artist": "Artist One",
        "album": {"name": "Album One"},
    }

    out = payloads.ensure_wishlist_track_format(track)

    assert out["name"] == "Song"
    assert out["artists"] == [{"name": "Artist One"}]
    assert out["album"]["name"] == "Album One"


def test_extract_spotify_track_from_modal_info_converts_trackresult_like_object():
    track_info = {
        "spotify_track": SimpleNamespace(
            title="Song Two",
            artist="Artist Two",
            album="Album Two",
        )
    }

    out = payloads.extract_spotify_track_from_modal_info(track_info)

    assert out["source"] == "trackresult"
    assert out["name"] == "Song Two"
    assert out["artists"] == [{"name": "Artist Two"}]
    assert out["album"]["name"] == "Album Two"


def test_extract_spotify_track_from_modal_info_reconstructs_from_slskd_result():
    track_info = {
        "slskd_result": SimpleNamespace(
            title="Song Three",
            artist="Artist Three",
            album="Album Three",
        )
    }

    out = payloads.extract_spotify_track_from_modal_info(track_info)

    assert out["reconstructed"] is True
    assert out["name"] == "Song Three"
    assert out["artists"] == [{"name": "Artist Three"}]
    assert out["album"]["name"] == "Album Three"


# ---------------------------------------------------------------------------
# track_number / disc_number preservation through the wishlist payload
# helpers — pins the bug A fix from PR 2/4. Pre-fix the helpers
# defaulted missing numbers to 1, which locked every wishlist retry
# to track 01 because the import pipeline's filename-extract fallback
# only fires when the value is None (not the pre-filled 1).
# ---------------------------------------------------------------------------


def test_ensure_wishlist_track_format_preserves_real_track_number():
    """Real track positions must survive the format helper. Pre-fix
    the helper read ``track_info.get('track_number', 1)`` which always
    returned 1 if the upstream payload had dropped the key — the
    desired number was lost on every round-trip."""
    track = {
        "name": "No Sleep Till Brooklyn",
        "artist": "Beastie Boys",
        "album": {"name": "Licensed to Ill", "release_date": "1986-11-15"},
        "track_number": 8,
        "disc_number": 1,
    }
    out = payloads.ensure_wishlist_track_format(track)
    assert out["track_number"] == 8
    assert out["disc_number"] == 1


def test_ensure_wishlist_track_format_keeps_missing_track_number_as_none():
    """When the upstream payload doesn't carry a track number, the
    helper must NOT pre-fill 1 — that poisons the chain and locks the
    file to track 01. Leave None so the import pipeline's filename
    fallback at ``core/imports/pipeline.py:652`` can fire."""
    track = {
        "name": "Mystery Track",
        "artist": "Artist",
        "album": {"name": "Album"},
    }
    out = payloads.ensure_wishlist_track_format(track)
    assert out["track_number"] is None
    assert out["disc_number"] is None


def test_build_cancelled_task_wishlist_payload_preserves_track_number():
    """Cancellation→re-add path was the worst offender — the payload
    builder dropped track_number from the saved data entirely (didn't
    even include the key). Next wishlist cycle saw missing key →
    helper defaulted to 1 → file imported as 01. Now both the
    cancellation payload AND the helper preserve real positions."""
    task = {
        "track_info": {
            "id": "trk-1", "name": "Brass Monkey",
            "artists": [{"name": "Beastie Boys"}],
            "album": {"name": "Licensed to Ill", "release_date": "1986-11-15"},
            "track_number": 11,
            "disc_number": 1,
        },
        "playlist_name": "Wishlist",
        "playlist_id": "p1",
    }
    out = payloads.build_cancelled_task_wishlist_payload(task)
    td = out["track_data"]
    assert td["track_number"] == 11
    assert td["disc_number"] == 1
    # Album release_date survives the round-trip so the path template
    # renders the year in the folder name.
    assert td["album"]["release_date"] == "1986-11-15"


def test_track_object_to_dict_preserves_full_album_dict():
    """When the input track has an album as a DICT (e.g. raw Spotify
    spotify_track_data), every album field must survive the
    Track→dict conversion. Pre-fix the conversion built
    ``album = {'name': X}`` only, silently dropping release_date /
    images / album_type / total_tracks. Result: every wishlist row
    added from a Track-object path had empty release_date in the DB
    → import path-template rendered without year → user's main
    complaint."""
    track_obj = SimpleNamespace(
        id="track-1",
        name="Never Gonna Give You Up",
        artists=[{"name": "Rick Astley"}],
        album={
            "id": "alb-1",
            "name": "Whenever You Need Somebody",
            "release_date": "1987-11-12",
            "album_type": "album",
            "total_tracks": 10,
            "images": [{"url": "https://cdn.example/cover.jpg"}],
            "artists": [{"name": "Rick Astley"}],
        },
        duration_ms=213000,
        track_number=1,
        disc_number=1,
    )
    out = payloads.track_object_to_dict(track_obj)
    assert out["album"]["name"] == "Whenever You Need Somebody"
    assert out["album"]["release_date"] == "1987-11-12"
    assert out["album"]["album_type"] == "album"
    assert out["album"]["total_tracks"] == 10
    assert out["album"]["id"] == "alb-1"
    assert out["album"]["images"] == [{"url": "https://cdn.example/cover.jpg"}]
    assert out["album"]["artists"] == [{"name": "Rick Astley"}]


def test_track_object_to_dict_extracts_release_date_from_album_object():
    """When the album is a sub-OBJECT (e.g. Spotify/Deezer Album
    dataclass), the conversion must pull release_date / album_type /
    total_tracks from its attributes — not assume dict-only access.
    Pre-fix the only attr read was ``.name``, dropping everything
    else even when the object had it."""

    class _AlbumLike:
        name = "Licensed to Ill"
        id = "alb-li"
        release_date = "1986-11-15"
        album_type = "album"
        total_tracks = 13
        artists = ["Beastie Boys"]
        images = [{"url": "https://cover.example/li.jpg"}]

    track_obj = SimpleNamespace(
        id="track-2",
        name="No Sleep Till Brooklyn",
        artists=[{"name": "Beastie Boys"}],
        album=_AlbumLike(),
        duration_ms=242000,
        track_number=8,
        disc_number=1,
    )
    out = payloads.track_object_to_dict(track_obj)
    assert out["album"]["name"] == "Licensed to Ill"
    assert out["album"]["release_date"] == "1986-11-15"
    assert out["album"]["total_tracks"] == 13
    assert out["album"]["id"] == "alb-li"
    assert out["track_number"] == 8


def test_track_object_to_dict_string_album_pulls_release_date_from_track_attrs():
    """When the album is a bare STRING (the lean Track dataclass
    shape used by some metadata sources), the album dict has to be
    built from scratch. Pull release_date + album_type from adjacent
    track-object attrs and image_url from the track itself so we
    don't lose the path-template inputs entirely."""
    track_obj = SimpleNamespace(
        id="track-3",
        name="Stacy's Mom",
        artists=[{"name": "Fountains of Wayne"}],
        album="Welcome Interstate Managers",
        release_date="2003-06-10",
        album_type="album",
        total_tracks=15,
        image_url="https://cover.example/wim.jpg",
        track_number=3,
        disc_number=1,
        duration_ms=200000,
    )
    out = payloads.track_object_to_dict(track_obj)
    assert out["album"]["name"] == "Welcome Interstate Managers"
    assert out["album"]["release_date"] == "2003-06-10"
    assert out["album"]["album_type"] == "album"
    assert out["album"]["total_tracks"] == 15
    assert out["album"]["images"] == [{"url": "https://cover.example/wim.jpg"}]
    assert out["track_number"] == 3


def test_track_object_to_dict_missing_track_number_stays_none():
    """Track-object-style sources that genuinely don't know the track
    position must surface as None (not pre-filled 1), so the import
    pipeline's filename-extract fallback can fire."""
    track_obj = SimpleNamespace(
        id="track-4",
        name="Unknown Track",
        artists=[{"name": "Artist"}],
        album="Album",
    )
    out = payloads.track_object_to_dict(track_obj)
    assert out["track_number"] is None
    assert out["disc_number"] is None


def test_build_cancelled_task_wishlist_payload_string_album_pulls_release_date_from_track_info():
    """When the source ``album`` field is a bare string, the payload
    builder constructs an album dict from scratch — it must pull
    release_date / album_image_url / etc. from the adjacent
    track_info fields rather than dropping them silently."""
    task = {
        "track_info": {
            "id": "trk-2", "name": "Song",
            "artists": [{"name": "Artist"}],
            "album": "Bare String Album",
            "release_date": "2020-06-01",
        },
    }
    out = payloads.build_cancelled_task_wishlist_payload(task)
    album = out["track_data"]["album"]
    assert album["name"] == "Bare String Album"
    assert album["release_date"] == "2020-06-01"


def test_ensure_wishlist_track_format_defaults_non_dict_album_to_album_type():
    """When ``album`` arrives as a non-dict (legacy/reconstruction path) we
    must not stamp ``album_type='single'`` — that lies about the origin
    and routes the wishlist requeue through the single_path template
    instead of album_path, dumping album tracks into the Singles tree.
    Default to 'album' / total_tracks=0 (unknown) so downstream code can
    fall through to the real release-type detection logic."""
    track = {
        "name": "Song",
        "artist": "Artist One",
        "album": "Album From Legacy String",
    }

    out = payloads.ensure_wishlist_track_format(track)

    assert out["album"]["name"] == "Album From Legacy String"
    assert out["album"]["album_type"] == "album"
    assert out["album"]["total_tracks"] == 0


def test_extract_spotify_track_from_modal_info_slskd_reconstruction_defaults_to_album():
    """Slskd-result reconstruction is a last-resort path; defaulting to
    ``album_type='single'`` corrupted the requeue routing for album
    batches. Same fix as ensure_wishlist_track_format: default 'album'."""
    track_info = {
        "slskd_result": SimpleNamespace(
            title="Song Three",
            artist="Artist Three",
            album="Album Three",
        )
    }

    out = payloads.extract_spotify_track_from_modal_info(track_info)

    assert out["album"]["album_type"] == "album"
    assert out["album"]["total_tracks"] == 0


def test_extract_wishlist_track_from_modal_info_uses_track_data_key():
    track_info = {
        "track_data": {
            "id": "track-1",
            "name": "Song Four",
            "artists": [{"name": "Artist Four"}],
            "album": {"name": "Album Four"},
        }
    }

    out = payloads.extract_wishlist_track_from_modal_info(track_info)

    assert out["id"] == "track-1"
    assert out["name"] == "Song Four"
    assert out["artists"] == [{"name": "Artist Four"}]
