import os
import sqlite3
from types import SimpleNamespace

import pytest

from core.imports import side_effects


class _FakeDB:
    def __init__(self, conn):
        self._conn = conn

    def _get_connection(self):
        return self._conn


def _make_soulsync_db():
    conn = sqlite3.connect(":memory:")
    conn.row_factory = sqlite3.Row
    conn.execute(
        """
        CREATE TABLE artists (
            id TEXT PRIMARY KEY,
            name TEXT,
            genres TEXT,
            thumb_url TEXT,
            server_source TEXT,
            created_at TEXT,
            updated_at TEXT,
            spotify_artist_id TEXT
        )
        """
    )
    conn.execute(
        """
        CREATE TABLE albums (
            id TEXT PRIMARY KEY,
            artist_id TEXT,
            title TEXT,
            year INTEGER,
            thumb_url TEXT,
            genres TEXT,
            track_count INTEGER,
            duration INTEGER,
            server_source TEXT,
            created_at TEXT,
            updated_at TEXT,
            spotify_album_id TEXT
        )
        """
    )
    conn.execute(
        """
        CREATE TABLE tracks (
            id TEXT PRIMARY KEY,
            album_id TEXT,
            artist_id TEXT,
            title TEXT,
            track_number INTEGER,
            duration INTEGER,
            file_path TEXT,
            bitrate INTEGER,
            file_size INTEGER,
            track_artist TEXT,
            musicbrainz_recording_id TEXT,
            isrc TEXT,
            server_source TEXT,
            created_at TEXT,
            updated_at TEXT,
            spotify_track_id TEXT,
            deezer_id TEXT
        )
        """
    )
    return conn


def test_record_soulsync_library_entry_writes_artist_album_and_track(tmp_path, monkeypatch):
    conn = _make_soulsync_db()
    fake_db = _FakeDB(conn)
    final_path = tmp_path / "track.flac"
    final_path.write_bytes(b"audio")

    monkeypatch.setattr(side_effects, "get_database", lambda: fake_db)
    monkeypatch.setattr(
        side_effects,
        "_get_config_manager",
        lambda: SimpleNamespace(get_active_media_server=lambda: "soulsync"),
    )

    import core.genre_filter as genre_filter

    monkeypatch.setattr(genre_filter, "filter_genres", lambda genres, _cfg: [genre.upper() for genre in genres])

    context = {
        "source": "spotify",
        "artist": {"id": "sp-artist", "name": "Artist One"},
        "album": {
            "id": "sp-album",
            "name": "Album One",
            "release_date": "2024-02-03",
            "total_tracks": 12,
            "image_url": "https://img.example/album.jpg",
        },
        "track_info": {
            "id": "sp-track",
            "name": "Song One",
            "track_number": 7,
            "duration_ms": 210000,
            "artists": [{"name": "Guest Artist"}],
            "_source": "spotify",
        },
        "original_search_result": {
            "title": "Song One",
            "artists": [{"name": "Guest Artist"}],
            "_source": "spotify",
        },
        "_final_processed_path": str(final_path),
    }

    artist_context = {"name": "Artist One", "genres": ["rock", "indie"]}
    album_info = {"is_album": True, "album_name": "Album One", "track_number": 7}

    side_effects.record_soulsync_library_entry(context, artist_context, album_info)

    artist_row = conn.execute("SELECT * FROM artists").fetchone()
    album_row = conn.execute("SELECT * FROM albums").fetchone()
    track_row = conn.execute("SELECT * FROM tracks").fetchone()

    assert artist_row["name"] == "Artist One"
    assert artist_row["server_source"] == "soulsync"
    assert artist_row["spotify_artist_id"] == "sp-artist"
    assert artist_row["genres"] == '["ROCK", "INDIE"]'

    assert album_row["title"] == "Album One"
    assert album_row["server_source"] == "soulsync"
    assert album_row["spotify_album_id"] == "sp-album"
    assert album_row["year"] == 2024
    assert album_row["track_count"] == 12
    assert album_row["duration"] == 210000
    assert album_row["artist_id"] == artist_row["id"]

    assert track_row["title"] == "Song One"
    assert track_row["server_source"] == "soulsync"
    assert track_row["spotify_track_id"] == "sp-track"
    assert track_row["track_number"] == 7
    assert track_row["duration"] == 210000
    assert track_row["track_artist"] == "Guest Artist"
    assert track_row["album_id"] == album_row["id"]
    assert track_row["file_path"] == str(final_path)
    # File size in bytes — populates the Library Disk Usage card on Stats.
    # Read via os.path.getsize at insert time since SoulSync standalone is
    # the only flow where the file is local at the moment we write the row.
    assert track_row["file_size"] == os.path.getsize(str(final_path))


def test_record_soulsync_library_entry_ignores_numeric_spotify_ids(tmp_path, monkeypatch):
    conn = _make_soulsync_db()
    fake_db = _FakeDB(conn)
    final_path = tmp_path / "track.flac"
    final_path.write_bytes(b"audio")

    monkeypatch.setattr(side_effects, "get_database", lambda: fake_db)
    monkeypatch.setattr(
        side_effects,
        "_get_config_manager",
        lambda: SimpleNamespace(get_active_media_server=lambda: "soulsync"),
    )

    import core.genre_filter as genre_filter

    monkeypatch.setattr(genre_filter, "filter_genres", lambda genres, _cfg: genres)

    context = {
        "source": "spotify",
        "artist": {"id": "396753", "name": "Artist One"},
        "album": {
            "id": "284076172",
            "name": "Album One",
            "release_date": "2024-02-03",
            "total_tracks": 12,
        },
        "track_info": {
            "id": "1607091752",
            "name": "Song One",
            "track_number": 7,
            "duration_ms": 210000,
            "artists": [{"name": "Guest Artist"}],
            "_source": "spotify",
        },
        "original_search_result": {
            "title": "Song One",
            "artists": [{"name": "Guest Artist"}],
            "_source": "spotify",
        },
        "_final_processed_path": str(final_path),
    }

    artist_context = {"name": "Artist One", "genres": ["rock"]}
    album_info = {"is_album": True, "album_name": "Album One", "track_number": 7}

    side_effects.record_soulsync_library_entry(context, artist_context, album_info)

    artist_row = conn.execute("SELECT * FROM artists").fetchone()
    album_row = conn.execute("SELECT * FROM albums").fetchone()
    track_row = conn.execute("SELECT * FROM tracks").fetchone()

    assert artist_row["spotify_artist_id"] is None
    assert album_row["spotify_album_id"] is None
    assert track_row["spotify_track_id"] is None


# ---------------------------------------------------------------------------
# SoulSync standalone parity — auto-import / direct download must write the
# same field richness a Plex/Jellyfin/Navidrome scan would write. Pin the
# per-recording identifier columns (`musicbrainz_recording_id`, `isrc`)
# AND the source-aware ID columns (`deezer_id`, etc.) for non-Spotify
# sources so dev work can't silently drop them.
# ---------------------------------------------------------------------------


def test_record_soulsync_library_entry_writes_mbid_and_isrc(tmp_path, monkeypatch):
    """Per-recording IDs land on the tracks row when the metadata source
    provides them (Picard-tagged libraries, MusicBrainz-enriched
    Spotify, etc.). Without this, watchlist re-download checks fall
    back to fuzzy name matching and re-download tracks the user
    already has."""
    conn = _make_soulsync_db()
    fake_db = _FakeDB(conn)
    final_path = tmp_path / "track.flac"
    final_path.write_bytes(b"audio")

    monkeypatch.setattr(side_effects, "get_database", lambda: fake_db)
    monkeypatch.setattr(
        side_effects,
        "_get_config_manager",
        lambda: SimpleNamespace(get_active_media_server=lambda: "soulsync"),
    )
    import core.genre_filter as genre_filter
    monkeypatch.setattr(genre_filter, "filter_genres", lambda genres, _cfg: genres)

    context = {
        "source": "spotify",
        "artist": {"id": "sp-artist", "name": "Picard Artist"},
        "album": {
            "id": "sp-album", "name": "Tagged Album",
            "release_date": "2022-01-01", "total_tracks": 10,
        },
        "track_info": {
            "id": "sp-track", "name": "Tagged Track",
            "track_number": 3, "duration_ms": 195000,
            "artists": [{"name": "Picard Artist"}],
            # Per-recording IDs — read by Mutagen from MUSICBRAINZ_TRACKID
            # tag (Picard) or surfaced from the metadata source's response.
            "musicbrainz_recording_id": "abcd1234-mbid-uuid-form",
            "isrc": "USABC1234567",
        },
        "original_search_result": {"title": "Tagged Track"},
        "_final_processed_path": str(final_path),
    }
    artist_context = {"name": "Picard Artist", "genres": []}
    album_info = {"is_album": True, "album_name": "Tagged Album", "track_number": 3}

    side_effects.record_soulsync_library_entry(context, artist_context, album_info)

    row = conn.execute("SELECT musicbrainz_recording_id, isrc FROM tracks").fetchone()
    assert row["musicbrainz_recording_id"] == "abcd1234-mbid-uuid-form"
    assert row["isrc"] == "USABC1234567"


def test_record_soulsync_library_entry_handles_deezer_source(tmp_path, monkeypatch):
    """Deezer source maps all three (artist/album/track) IDs onto the
    `deezer_id` column. Verify the source-aware column resolver routes
    correctly — a regression here means deezer-primary users get
    soulsync rows with no source ID at all."""
    conn = _make_soulsync_db()
    fake_db = _FakeDB(conn)
    final_path = tmp_path / "track.flac"
    final_path.write_bytes(b"audio")

    monkeypatch.setattr(side_effects, "get_database", lambda: fake_db)
    monkeypatch.setattr(
        side_effects,
        "_get_config_manager",
        lambda: SimpleNamespace(get_active_media_server=lambda: "soulsync"),
    )
    import core.genre_filter as genre_filter
    monkeypatch.setattr(genre_filter, "filter_genres", lambda genres, _cfg: genres)

    context = {
        "source": "deezer",
        "artist": {"id": "12345", "name": "DZ Artist"},
        "album": {"id": "67890", "name": "DZ Album", "total_tracks": 5},
        "track_info": {
            "id": "111213",
            "name": "DZ Track",
            "track_number": 1,
            "duration_ms": 180000,
            "artists": [{"name": "DZ Artist"}],
        },
        "original_search_result": {"title": "DZ Track"},
        "_final_processed_path": str(final_path),
    }
    artist_context = {"name": "DZ Artist", "genres": []}
    album_info = {"is_album": True, "album_name": "DZ Album", "track_number": 1}

    side_effects.record_soulsync_library_entry(context, artist_context, album_info)

    track_row = conn.execute("SELECT deezer_id FROM tracks").fetchone()
    # Deezer source map writes the track's source-id onto the deezer_id
    # column (same column name the artist + album use; deezer doesn't
    # split per-entity-type ID columns the way Spotify / iTunes do).
    assert track_row["deezer_id"] == "111213"


# ---------------------------------------------------------------------------
# Auto-import labelling — library history + provenance must show
# "Auto-Import" / "auto_import" instead of falling back to "Soulseek".
# ---------------------------------------------------------------------------


def _make_history_db():
    conn = sqlite3.connect(":memory:")
    conn.row_factory = sqlite3.Row
    conn.execute(
        """
        CREATE TABLE library_history (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            event_type TEXT, title TEXT, artist_name TEXT, album_name TEXT,
            quality TEXT, file_path TEXT, thumb_url TEXT, download_source TEXT,
            source_track_id TEXT, source_track_title TEXT, source_filename TEXT,
            acoustid_result TEXT, source_artist TEXT, created_at TEXT
        )
        """
    )
    return conn


def test_library_history_labels_auto_import(monkeypatch):
    """Auto-import sets `_download_username='auto_import'`; history row
    must read 'Auto-Import' instead of falling back to 'Soulseek'."""
    conn = _make_history_db()

    captured = {}

    class _DBStub:
        def add_library_history_entry(self, **kwargs):
            captured.update(kwargs)

    monkeypatch.setattr(side_effects, "get_database", lambda: _DBStub())

    context = {
        "_download_username": "auto_import",
        "track_info": {
            "name": "Auto-Imported Track",
            "artists": [{"name": "Some Artist"}],
            "album": "Some Album",
            "id": "abc",
        },
        "original_search_result": {},
        "_final_processed_path": "/library/some-album/01.flac",
    }
    side_effects.record_library_history_download(context)
    assert captured["download_source"] == "Auto-Import"
    assert captured["title"] == "Auto-Imported Track"


@pytest.mark.parametrize(
    ("username", "expected"),
    [
        ("torrent", "Torrent"),
        ("usenet", "Usenet"),
        ("staging", "Staging"),
    ],
)
def test_library_history_labels_release_and_staging_sources(monkeypatch, username, expected):
    """Release/staging imports should not fall through to the Soulseek label."""
    captured = {}

    class _DBStub:
        def add_library_history_entry(self, **kwargs):
            captured.update(kwargs)

    monkeypatch.setattr(side_effects, "get_database", lambda: _DBStub())

    context = {
        "track_info": {
            "name": "Imported Track",
            "artists": [{"name": "Some Artist"}],
            "album": "Some Album",
        },
        "original_search_result": {
            "username": username,
            "filename": "source-file.flac",
        },
        "_final_processed_path": "/library/some-album/01.flac",
    }

    side_effects.record_library_history_download(context)

    assert captured["download_source"] == expected


# ---------------------------------------------------------------------------
# Album duration parity — must equal sum of all track durations, not whatever
# the first imported track happened to be.
# ---------------------------------------------------------------------------


def test_album_duration_uses_album_total_not_single_track(tmp_path, monkeypatch):
    """Pre-fix `record_soulsync_library_entry` wrote
    `track_info.duration_ms` (one track's duration) into the album row's
    `duration` column. SoulSync standalone scanner sums every track's
    duration to populate that column — mirror it. This test passes
    `album.duration_ms` explicitly on the context (the worker computes
    it as `sum(match['track']['duration_ms'])`) and verifies the album
    row reads it instead of falling back to the per-track value."""
    conn = _make_soulsync_db()
    fake_db = _FakeDB(conn)
    final_path = tmp_path / "track5.flac"
    final_path.write_bytes(b"audio")

    monkeypatch.setattr(side_effects, "get_database", lambda: fake_db)
    monkeypatch.setattr(
        side_effects,
        "_get_config_manager",
        lambda: SimpleNamespace(get_active_media_server=lambda: "soulsync"),
    )
    import core.genre_filter as genre_filter
    monkeypatch.setattr(genre_filter, "filter_genres", lambda genres, _cfg: genres)

    context = {
        "source": "spotify",
        "artist": {"id": "sp-artist", "name": "Artist"},
        "album": {
            "id": "sp-album",
            "name": "Long Album",
            "release_date": "2024-01-01",
            "total_tracks": 12,
            # Sum across the album — the worker computes this from
            # match_result.matches. Single-track payload below is
            # 200_000ms but album total is 2_500_000.
            "duration_ms": 2_500_000,
        },
        "track_info": {
            "id": "sp-track-5",
            "name": "Track 5",
            "track_number": 5,
            "duration_ms": 200_000,
            "artists": [{"name": "Artist"}],
        },
        "original_search_result": {"title": "Track 5"},
        "_final_processed_path": str(final_path),
    }
    artist_context = {"name": "Artist", "genres": []}
    album_info = {"is_album": True, "album_name": "Long Album", "track_number": 5}

    side_effects.record_soulsync_library_entry(context, artist_context, album_info)

    album_row = conn.execute("SELECT duration FROM albums").fetchone()
    track_row = conn.execute("SELECT duration FROM tracks").fetchone()
    assert album_row["duration"] == 2_500_000, (
        f"Album duration must equal album total, got {album_row['duration']}. "
        f"Bug: it's writing the single track's duration (200_000) instead."
    )
    assert track_row["duration"] == 200_000


# ---------------------------------------------------------------------------
# Conservative UPDATE path — second import refreshes empty fields without
# clobbering populated ones.
# ---------------------------------------------------------------------------


def test_re_import_fills_empty_artist_fields(tmp_path, monkeypatch):
    """First import lands an artist row with no thumb_url + no genres
    (e.g. genre tags were absent on those tracks). Second import for
    the SAME artist comes in with thumb_url + genres present — those
    must land on the existing row instead of being silently ignored
    (the pre-fix behaviour was insert-only)."""
    conn = _make_soulsync_db()
    fake_db = _FakeDB(conn)
    final_path1 = tmp_path / "first.flac"
    final_path1.write_bytes(b"audio")
    final_path2 = tmp_path / "second.flac"
    final_path2.write_bytes(b"audio")

    monkeypatch.setattr(side_effects, "get_database", lambda: fake_db)
    monkeypatch.setattr(
        side_effects,
        "_get_config_manager",
        lambda: SimpleNamespace(get_active_media_server=lambda: "soulsync"),
    )
    import core.genre_filter as genre_filter
    monkeypatch.setattr(genre_filter, "filter_genres", lambda genres, _cfg: genres)

    # First import — artist with no thumb_url + no genres
    ctx1 = {
        "source": "spotify",
        "artist": {"id": "sp-artist", "name": "Same Artist"},
        "album": {"id": "sp-album-1", "name": "First Album", "total_tracks": 1},
        "track_info": {"id": "sp-track-1", "name": "T1", "track_number": 1,
                       "duration_ms": 200000, "artists": [{"name": "Same Artist"}]},
        "original_search_result": {},
        "_final_processed_path": str(final_path1),
    }
    side_effects.record_soulsync_library_entry(
        ctx1,
        {"name": "Same Artist", "genres": []},  # NO genres
        {"is_album": True, "album_name": "First Album", "track_number": 1},
    )

    artist_row = conn.execute("SELECT id, thumb_url, genres FROM artists").fetchone()
    artist_id_first = artist_row["id"]
    assert artist_row["thumb_url"] in (None, "")
    assert artist_row["genres"] in (None, "")

    # Second import — artist with thumb + genres present
    ctx2 = dict(ctx1)
    ctx2["album"] = {"id": "sp-album-2", "name": "Second Album", "total_tracks": 1,
                     "image_url": "https://img.example/cover2.jpg"}
    ctx2["track_info"] = {"id": "sp-track-2", "name": "T2", "track_number": 1,
                          "duration_ms": 200000, "artists": [{"name": "Same Artist"}]}
    ctx2["_final_processed_path"] = str(final_path2)
    side_effects.record_soulsync_library_entry(
        ctx2,
        {"name": "Same Artist", "genres": ["Hip-Hop", "Rap"]},
        {"is_album": True, "album_name": "Second Album", "track_number": 1},
    )

    # Same artist row updated — empty fields filled
    artist_row2 = conn.execute("SELECT id, thumb_url, genres FROM artists").fetchone()
    assert artist_row2["id"] == artist_id_first, "Should reuse existing artist row"
    assert artist_row2["thumb_url"] == "https://img.example/cover2.jpg", (
        "Empty thumb_url should be filled from second import"
    )
    assert "Hip-Hop" in (artist_row2["genres"] or ""), (
        "Empty genres should be filled from second import"
    )
    # Two album rows now (different albums for same artist)
    album_count = conn.execute("SELECT COUNT(*) FROM albums").fetchone()[0]
    assert album_count == 2


def test_re_import_does_not_clobber_populated_artist_fields(tmp_path, monkeypatch):
    """First import with rich genres + thumb. Second import with
    DIFFERENT (worse) genres + DIFFERENT thumb. Existing populated
    values must be preserved, not overwritten — protects manual
    edits + enrichment-worker writes."""
    conn = _make_soulsync_db()
    fake_db = _FakeDB(conn)
    final_path1 = tmp_path / "first.flac"
    final_path1.write_bytes(b"audio")
    final_path2 = tmp_path / "second.flac"
    final_path2.write_bytes(b"audio")

    monkeypatch.setattr(side_effects, "get_database", lambda: fake_db)
    monkeypatch.setattr(
        side_effects,
        "_get_config_manager",
        lambda: SimpleNamespace(get_active_media_server=lambda: "soulsync"),
    )
    import core.genre_filter as genre_filter
    monkeypatch.setattr(genre_filter, "filter_genres", lambda genres, _cfg: genres)

    ctx1 = {
        "source": "spotify",
        "artist": {"id": "sp-artist", "name": "Stable Artist"},
        "album": {"id": "sp-album-1", "name": "A1", "total_tracks": 1,
                  "image_url": "https://img.example/original.jpg"},
        "track_info": {"id": "sp-track-1", "name": "T1", "track_number": 1,
                       "duration_ms": 200000, "artists": [{"name": "Stable Artist"}]},
        "original_search_result": {},
        "_final_processed_path": str(final_path1),
    }
    side_effects.record_soulsync_library_entry(
        ctx1,
        {"name": "Stable Artist", "genres": ["Hip-Hop", "Rap", "Trap"]},
        {"is_album": True, "album_name": "A1", "track_number": 1},
    )

    # Second import with worse / different metadata
    ctx2 = dict(ctx1)
    ctx2["album"] = {"id": "sp-album-2", "name": "A2", "total_tracks": 1,
                     "image_url": "https://img.example/replacement.jpg"}
    ctx2["track_info"] = {"id": "sp-track-2", "name": "T2", "track_number": 1,
                          "duration_ms": 200000, "artists": [{"name": "Stable Artist"}]}
    ctx2["_final_processed_path"] = str(final_path2)
    side_effects.record_soulsync_library_entry(
        ctx2,
        {"name": "Stable Artist", "genres": ["Pop"]},  # Different + worse
        {"is_album": True, "album_name": "A2", "track_number": 1},
    )

    artist_row = conn.execute("SELECT thumb_url, genres FROM artists").fetchone()
    # Original values preserved
    assert artist_row["thumb_url"] == "https://img.example/original.jpg", (
        "Existing thumb_url must NOT be clobbered by re-import"
    )
    assert "Hip-Hop" in artist_row["genres"], (
        "Existing genres must NOT be clobbered by re-import"
    )
    # NEW values must NOT have replaced the originals
    assert "replacement" not in (artist_row["thumb_url"] or "")
    assert "Pop" not in (artist_row["genres"] or "")


def test_re_import_fills_empty_source_id_when_missing(tmp_path, monkeypatch):
    """First import via fingerprint identification — no spotify_track_id
    on the artist row. Second import (same artist) via tag-based match
    that DOES carry a spotify_artist_id. The fill-empty UPDATE must
    populate the column."""
    conn = _make_soulsync_db()
    fake_db = _FakeDB(conn)
    final_path1 = tmp_path / "first.flac"
    final_path1.write_bytes(b"audio")
    final_path2 = tmp_path / "second.flac"
    final_path2.write_bytes(b"audio")

    monkeypatch.setattr(side_effects, "get_database", lambda: fake_db)
    monkeypatch.setattr(
        side_effects,
        "_get_config_manager",
        lambda: SimpleNamespace(get_active_media_server=lambda: "soulsync"),
    )
    import core.genre_filter as genre_filter
    monkeypatch.setattr(genre_filter, "filter_genres", lambda genres, _cfg: genres)

    # First import — no source artist ID
    ctx1 = {
        "source": "spotify",
        "artist": {"id": "", "name": "Same Artist"},  # No ID
        "album": {"id": "sp-album-1", "name": "A1", "total_tracks": 1},
        "track_info": {"id": "sp-track-1", "name": "T1", "track_number": 1,
                       "duration_ms": 200000, "artists": [{"name": "Same Artist"}]},
        "original_search_result": {},
        "_final_processed_path": str(final_path1),
    }
    side_effects.record_soulsync_library_entry(
        ctx1, {"name": "Same Artist", "genres": []},
        {"is_album": True, "album_name": "A1", "track_number": 1},
    )

    artist_row = conn.execute("SELECT spotify_artist_id FROM artists").fetchone()
    assert artist_row["spotify_artist_id"] in (None, "")

    # Second import — now carries a valid source ID
    ctx2 = dict(ctx1)
    ctx2["artist"] = {"id": "sp-artist-real", "name": "Same Artist"}
    ctx2["album"] = {"id": "sp-album-2", "name": "A2", "total_tracks": 1}
    ctx2["track_info"] = {"id": "sp-track-2", "name": "T2", "track_number": 1,
                          "duration_ms": 200000, "artists": [{"name": "Same Artist"}]}
    ctx2["_final_processed_path"] = str(final_path2)
    side_effects.record_soulsync_library_entry(
        ctx2, {"name": "Same Artist", "genres": []},
        {"is_album": True, "album_name": "A2", "track_number": 1},
    )

    artist_row2 = conn.execute("SELECT spotify_artist_id FROM artists").fetchone()
    assert artist_row2["spotify_artist_id"] == "sp-artist-real", (
        "Empty spotify_artist_id should be filled by the second import. "
        "This is what makes the watchlist scanner recognise the artist "
        "as already in library by stable source ID."
    )


def test_provenance_labels_auto_import(monkeypatch):
    """Same gate for provenance: `_download_username='auto_import'`
    must register the provenance row as `auto_import` (lowercase /
    canonical), not the `soulseek` fallback default."""
    captured = {}

    class _DBStub:
        def record_track_download(self, **kwargs):
            captured.update(kwargs)

    monkeypatch.setattr(side_effects, "get_database", lambda: _DBStub())

    context = {
        "_download_username": "auto_import",
        "track_info": {
            "name": "Auto-Imported Track",
            "artists": [{"name": "Some Artist"}],
            "album": "Some Album",
            "id": "abc",
        },
        "original_search_result": {},
        "_final_processed_path": "/library/some-album/01.flac",
    }
    side_effects.record_download_provenance(context)
    assert captured.get("source_service") == "auto_import"
