import sys
import types


if "spotipy" not in sys.modules:
    spotipy = types.ModuleType("spotipy")

    class _DummySpotify:
        def __init__(self, *args, **kwargs):
            pass

    oauth2 = types.ModuleType("spotipy.oauth2")

    class _DummyOAuth:
        def __init__(self, *args, **kwargs):
            pass

    spotipy.Spotify = _DummySpotify
    oauth2.SpotifyOAuth = _DummyOAuth
    oauth2.SpotifyClientCredentials = _DummyOAuth
    spotipy.oauth2 = oauth2
    sys.modules["spotipy"] = spotipy
    sys.modules["spotipy.oauth2"] = oauth2

if "config.settings" not in sys.modules:
    config_pkg = types.ModuleType("config")
    settings_mod = types.ModuleType("config.settings")

    class _DummyConfigManager:
        def get(self, key, default=None):
            return default

        def get_active_media_server(self):
            return "primary"

    settings_mod.config_manager = _DummyConfigManager()
    config_pkg.settings = settings_mod
    sys.modules["config"] = config_pkg
    sys.modules["config.settings"] = settings_mod

import types as pytypes

from core.metadata import registry as metadata_registry
from core.metadata import similar_artists as metadata_similar_artists


class _FakeMusicMapResponse:
    def __init__(self, text):
        self.text = text

    def raise_for_status(self):
        return None


class _FakeSourceClient:
    def __init__(self, results_by_query):
        self.results_by_query = results_by_query
        self.search_calls = []

    def search_artists(self, query, **kwargs):
        self.search_calls.append((query, dict(kwargs)))
        return list(self.results_by_query.get(query, []))


def test_iter_musicmap_similar_artist_events_uses_source_priority(monkeypatch):
    html = """
    <html>
      <body>
        <div id="gnodMap">
          <a href="/artist/seed">Artist One</a>
          <a href="/artist/similar">Similar Artist</a>
        </div>
      </body>
    </html>
    """

    deezer = _FakeSourceClient({
        "Artist One": [pytypes.SimpleNamespace(id="dz-seed", name="Artist One")],
        "Similar Artist": [],
    })
    itunes = _FakeSourceClient({
        "Artist One": [pytypes.SimpleNamespace(id="it-seed", name="Artist One")],
        "Similar Artist": [
            pytypes.SimpleNamespace(
                id="it-match",
                name="iTunes Canonical",
                image_url="https://itunes.example/it-match.jpg",
                genres=["indie", "alt"],
                popularity=77,
            )
        ],
    })
    spotify = _FakeSourceClient({})

    monkeypatch.setattr(metadata_similar_artists.requests, "get", lambda *args, **kwargs: _FakeMusicMapResponse(html))
    monkeypatch.setattr(metadata_registry, "get_primary_source", lambda spotify_client_factory=None: "deezer")
    monkeypatch.setattr(metadata_registry, "get_source_priority", lambda primary: [primary, "itunes", "spotify"])
    monkeypatch.setattr(
        metadata_registry,
        "get_client_for_source",
        lambda source, **kwargs: {"deezer": deezer, "itunes": itunes, "spotify": spotify}.get(source),
    )

    events = list(metadata_similar_artists.iter_musicmap_similar_artist_events("Artist One", limit=5))

    assert events[0]["type"] == "start"
    assert events[0]["source_priority"] == ["deezer", "itunes", "spotify"]
    assert events[1]["type"] == "artist"
    assert events[1]["artist"]["id"] == "it-match"
    assert events[1]["artist"]["name"] == "iTunes Canonical"
    assert events[1]["artist"]["image_url"] == "https://itunes.example/it-match.jpg"
    assert events[1]["artist"]["genres"] == ["indie", "alt"]
    assert events[1]["artist"]["popularity"] == 77
    assert events[1]["artist"]["source"] == "itunes"
    assert events[-1]["type"] == "complete"
    assert events[-1]["complete"] is True
    assert events[-1]["total"] == 1
    assert events[-1]["total_found"] == 1

    assert [call[0] for call in deezer.search_calls] == ["Artist One", "Similar Artist"]
    assert [call[0] for call in itunes.search_calls] == ["Artist One", "Similar Artist"]
    assert spotify.search_calls == [("Artist One", {"limit": 1, "allow_fallback": False})]


def test_iter_musicmap_similar_artist_events_enriches_itunes_images(monkeypatch):
    html = """
    <html>
      <body>
        <div id="gnodMap">
          <a href="/artist/similar">Similar Artist</a>
        </div>
      </body>
    </html>
    """

    class _ItunesClient(_FakeSourceClient):
        def __init__(self):
            super().__init__({
                "Artist One": [pytypes.SimpleNamespace(id="it-seed", name="Artist One")],
                "Similar Artist": [pytypes.SimpleNamespace(id="it-match", name="iTunes Canonical")],
            })
            self.get_artist_calls = []

        def get_artist(self, artist_id):
            self.get_artist_calls.append(artist_id)
            return {
                "id": artist_id,
                "name": "iTunes Canonical",
                "images": [{"url": "https://itunes.example/full-art.jpg"}],
                "genres": ["indie"],
                "popularity": 0,
            }

    itunes = _ItunesClient()
    monkeypatch.setattr(metadata_similar_artists.requests, "get", lambda *args, **kwargs: _FakeMusicMapResponse(html))
    monkeypatch.setattr(metadata_registry, "get_primary_source", lambda spotify_client_factory=None: "itunes")
    monkeypatch.setattr(metadata_registry, "get_source_priority", lambda primary: [primary])
    monkeypatch.setattr(metadata_registry, "get_client_for_source", lambda source, **kwargs: itunes if source == "itunes" else None)

    events = list(metadata_similar_artists.iter_musicmap_similar_artist_events("Artist One", limit=5))

    artist_events = [event for event in events if event.get("type") == "artist"]
    assert len(artist_events) == 1
    assert artist_events[0]["artist"]["image_url"] == "https://itunes.example/full-art.jpg"
    assert itunes.get_artist_calls == ["it-match"]


def test_iter_musicmap_similar_artist_events_falls_back_to_itunes_album_art(monkeypatch):
    html = """
    <html>
      <body>
        <div id="gnodMap">
          <a href="/artist/similar">Similar Artist</a>
        </div>
      </body>
    </html>
    """

    class _ItunesClient(_FakeSourceClient):
        def __init__(self):
            super().__init__({
                "Artist One": [pytypes.SimpleNamespace(id="it-seed", name="Artist One")],
                "Similar Artist": [pytypes.SimpleNamespace(id="it-match", name="iTunes Canonical")],
            })
            self.get_artist_calls = []
            self.album_art_calls = []

        def get_artist(self, artist_id):
            self.get_artist_calls.append(artist_id)
            return {
                "id": artist_id,
                "name": "iTunes Canonical",
                "images": [],
                "genres": ["indie"],
                "popularity": 0,
            }

        def _get_artist_image_from_albums(self, artist_id):
            self.album_art_calls.append(artist_id)
            return "https://itunes.example/album-art.jpg"

    itunes = _ItunesClient()
    monkeypatch.setattr(metadata_similar_artists.requests, "get", lambda *args, **kwargs: _FakeMusicMapResponse(html))
    monkeypatch.setattr(metadata_registry, "get_primary_source", lambda spotify_client_factory=None: "itunes")
    monkeypatch.setattr(metadata_registry, "get_source_priority", lambda primary: [primary])
    monkeypatch.setattr(metadata_registry, "get_client_for_source", lambda source, **kwargs: itunes if source == "itunes" else None)

    events = list(metadata_similar_artists.iter_musicmap_similar_artist_events("Artist One", limit=5))

    artist_events = [event for event in events if event.get("type") == "artist"]
    assert len(artist_events) == 1
    assert artist_events[0]["artist"]["image_url"] == "https://itunes.example/album-art.jpg"
    assert itunes.get_artist_calls == ["it-match"]
    assert itunes.album_art_calls == ["it-match"]


def test_get_musicmap_similar_artists_returns_not_found_when_musicmap_missing(monkeypatch):
    html = """
    <html>
      <body>
        <div class="no-map">Nothing here</div>
      </body>
    </html>
    """

    monkeypatch.setattr(metadata_similar_artists.requests, "get", lambda *args, **kwargs: _FakeMusicMapResponse(html))
    monkeypatch.setattr(metadata_registry, "get_primary_source", lambda spotify_client_factory=None: "deezer")
    monkeypatch.setattr(metadata_registry, "get_source_priority", lambda primary: [primary, "itunes"])
    monkeypatch.setattr(metadata_registry, "get_client_for_source", lambda source, **kwargs: object())

    result = metadata_similar_artists.get_musicmap_similar_artists("Artist One", limit=5)

    assert result["success"] is False
    assert result["status_code"] == 404
    assert "Could not find artist map" in result["error"]
    assert result["similar_artists"] == []
