import sqlite3
import sys
import types
from types import SimpleNamespace

# Stub optional Spotify dependency so metadata_service can import in tests.
if 'spotipy' not in sys.modules:
    spotipy = types.ModuleType('spotipy')
    oauth2 = types.ModuleType('spotipy.oauth2')

    class _DummySpotify:
        pass

    class _DummyOAuth:
        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_mod = 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 "plex"

    settings_mod.config_manager = _DummyConfigManager()
    config_mod.settings = settings_mod
    sys.modules['config'] = config_mod
    sys.modules['config.settings'] = settings_mod

from core.repair_jobs import missing_cover_art as mca


class _FakeClient:
    def __init__(self, album_image=None, search_image=None):
        self.album_image = album_image
        self.search_image = search_image
        self.get_album_calls = []
        self.search_calls = []

    def get_album(self, album_id, include_tracks=False):
        self.get_album_calls.append((album_id, include_tracks))
        if self.album_image is None:
            return None
        if isinstance(self.album_image, str):
            return {'images': [{'url': self.album_image}]}
        return self.album_image

    def search_albums(self, query, limit=1):
        self.search_calls.append((query, limit))
        if self.search_image is None:
            return []
        return [SimpleNamespace(id='search-album', image_url=self.search_image)]


def _make_db(album_row):
    conn = sqlite3.connect(':memory:')
    conn.row_factory = sqlite3.Row
    cursor = conn.cursor()
    cursor.execute(
        """
        CREATE TABLE artists (
            id INTEGER PRIMARY KEY,
            name TEXT,
            thumb_url TEXT
        )
        """
    )
    cursor.execute(
        """
        CREATE TABLE albums (
            id INTEGER PRIMARY KEY,
            title TEXT,
            artist_id INTEGER,
            thumb_url TEXT,
            spotify_album_id TEXT,
            itunes_album_id TEXT,
            deezer_id TEXT,
            discogs_id TEXT,
            soul_id TEXT
        )
        """
    )
    cursor.execute(
        "INSERT INTO artists (id, name, thumb_url) VALUES (?, ?, ?)",
        (1, 'Artist', 'https://artist/thumb'),
    )
    cursor.execute(
        """
        INSERT INTO albums
            (id, title, artist_id, thumb_url, spotify_album_id, itunes_album_id, deezer_id, discogs_id, soul_id)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        """,
        album_row,
    )
    conn.commit()
    return conn


def _make_context(conn, prefer_source=None):
    job_settings = {}
    if prefer_source is not None:
        job_settings['prefer_source'] = prefer_source
    settings = {'repair.jobs.missing_cover_art.settings': job_settings}
    findings = []
    return SimpleNamespace(
        db=SimpleNamespace(_get_connection=lambda: conn),
        config_manager=SimpleNamespace(get=lambda key, default=None: settings.get(key, default)),
        check_stop=lambda: False,
        wait_if_paused=lambda: False,
        update_progress=lambda *args, **kwargs: None,
        report_progress=lambda *args, **kwargs: None,
        # Mirror real `_create_finding` contract: True on insert.
        create_finding=lambda **kwargs: (findings.append(kwargs) or True),
        findings=findings,
    )


def test_missing_cover_art_prefers_explicit_source_over_primary(monkeypatch):
    conn = _make_db((1, 'Album', 1, '', 'sp-album', 'it-album', 'dz-album', 'dg-album', 'hy-album'))
    context = _make_context(conn, prefer_source='spotify')

    deezer_client = _FakeClient(album_image='https://img/deezer-direct')
    spotify_client = _FakeClient(album_image='https://img/spotify-direct')

    monkeypatch.setattr(mca, 'get_primary_source', lambda: 'deezer')
    monkeypatch.setattr(
        mca,
        'get_client_for_source',
        lambda source: {'deezer': deezer_client, 'spotify': spotify_client}.get(source),
    )

    result = mca.MissingCoverArtJob().scan(context)

    assert result.findings_created == 1
    assert spotify_client.get_album_calls == [('sp-album', False)]
    assert deezer_client.get_album_calls == []
    assert context.findings[0]['details']['found_artwork_url'] == 'https://img/spotify-direct'


def test_missing_cover_art_uses_primary_when_prefer_unset(monkeypatch):
    conn = _make_db((1, 'Album', 1, '', None, None, None, None, None))
    context = _make_context(conn)

    discogs_client = _FakeClient(search_image='https://img/discogs-search')
    spotify_client = _FakeClient(search_image='https://img/spotify-search')
    itunes_client = _FakeClient(search_image='https://img/itunes-search')

    monkeypatch.setattr(mca, 'get_primary_source', lambda: 'discogs')
    monkeypatch.setattr(
        mca,
        'get_client_for_source',
        lambda source: {'discogs': discogs_client, 'spotify': spotify_client, 'itunes': itunes_client}.get(source),
    )

    result = mca.MissingCoverArtJob().scan(context)

    assert result.findings_created == 1
    assert discogs_client.search_calls == [('Artist Album', 1)]
    assert spotify_client.search_calls == []
    assert itunes_client.search_calls == []
    assert context.findings[0]['details']['found_artwork_url'] == 'https://img/discogs-search'
