"""Phase A pinning tests for DeezerDownloadClient — UPDATED for Phase C6.

Deezer has the same engine-driven dispatch as the other streaming
sources, with three Deezer-specific quirks preserved:
- track_id stays as STRING (Deezer GW API uses string IDs).
- Engine record's `username` slot is the legacy `'deezer_dl'`
  via worker username_override.
- Worker thread is named `deezer-dl-<track_id>` for diagnostics.
"""

from __future__ import annotations

import asyncio
import threading
from pathlib import Path
from unittest.mock import patch

import pytest

from core.download_engine import DownloadEngine
from core.deezer_download_client import DeezerDownloadClient


def _run_async(coro):
    loop = asyncio.new_event_loop()
    try:
        return loop.run_until_complete(coro)
    finally:
        loop.close()


@pytest.fixture
def deezer_client_with_engine():
    client = DeezerDownloadClient.__new__(DeezerDownloadClient)
    client.download_path = Path('./test_deezer_downloads')
    client.shutdown_check = None
    client._authenticated = True
    client._engine = None
    engine = DownloadEngine()
    client.set_engine(engine)
    return client, engine


def test_download_returns_none_when_not_authenticated(deezer_client_with_engine):
    client, _ = deezer_client_with_engine
    client._authenticated = False
    result = _run_async(client.download('deezer_dl', '12345||x', 0))
    assert result is None


def test_download_raises_when_engine_not_wired():
    """Defensive: client without engine reference must raise so the
    orchestrator's download_with_fallback surfaces the error and
    moves on to the next source. Returning None silently would drop
    the download with no user feedback (per JohnBaumb)."""
    import pytest
    client = DeezerDownloadClient.__new__(DeezerDownloadClient)
    client._engine = None
    # Bypass auth gate so we exercise the engine check.
    client._authenticated = True
    with pytest.raises(RuntimeError, match="engine reference"):
        _run_async(client.download('deezer', 'v||t', 0))


def test_download_track_id_stays_as_string(deezer_client_with_engine):
    """Pinning: Deezer GW API uses string IDs — engine record must
    keep track_id as str."""
    client, engine = deezer_client_with_engine
    started = threading.Event()
    release = threading.Event()

    def slow_impl(*args, **kwargs):
        started.set()
        release.wait(timeout=1.0)
        return '/tmp/done.flac'

    with patch.object(client, '_download_sync', side_effect=slow_impl):
        download_id = _run_async(client.download('deezer_dl', '999||X', 0))
        started.wait(timeout=1.0)
        record = engine.get_record('deezer', download_id)
        assert record['track_id'] == '999'
        assert isinstance(record['track_id'], str)
        release.set()


def test_download_username_slot_is_legacy_deezer_dl(deezer_client_with_engine):
    """Pinning: frontend status indicators key off `'deezer_dl'`,
    not the canonical `'deezer'`."""
    client, engine = deezer_client_with_engine
    started = threading.Event()
    release = threading.Event()

    def slow_impl(*args, **kwargs):
        started.set()
        release.wait(timeout=1.0)
        return '/tmp/done.flac'

    with patch.object(client, '_download_sync', side_effect=slow_impl):
        download_id = _run_async(client.download('deezer_dl', '999||x', 0))
        started.wait(timeout=1.0)
        assert engine.get_record('deezer', download_id)['username'] == 'deezer_dl'
        release.set()


def test_download_handles_missing_display_name_with_fallback(deezer_client_with_engine):
    """Pinning: filename without `||` synthesizes display name `Track <id>`."""
    client, engine = deezer_client_with_engine
    started = threading.Event()
    release = threading.Event()

    def slow_impl(*args, **kwargs):
        started.set()
        release.wait(timeout=1.0)
        return '/tmp/x.flac'

    with patch.object(client, '_download_sync', side_effect=slow_impl):
        download_id = _run_async(client.download('deezer_dl', '12345', 0))
        started.wait(timeout=1.0)
        assert engine.get_record('deezer', download_id)['display_name'] == 'Track 12345'
        release.set()


def test_download_engine_record_carries_error_slot(deezer_client_with_engine):
    """Pinning: Deezer-specific `error` slot for ARL re-auth failure
    messages must be present on init."""
    client, engine = deezer_client_with_engine
    started = threading.Event()
    release = threading.Event()

    def slow_impl(*args, **kwargs):
        started.set()
        release.wait(timeout=1.0)
        return '/tmp/x.flac'

    with patch.object(client, '_download_sync', side_effect=slow_impl):
        download_id = _run_async(client.download('deezer_dl', '999||X', 1024))
        started.wait(timeout=1.0)
        record = engine.get_record('deezer', download_id)
        assert 'error' in record
        assert record['error'] is None
        assert record['size'] == 1024
        release.set()


def test_get_all_downloads_reads_engine_records(deezer_client_with_engine):
    client, engine = deezer_client_with_engine
    engine.add_record('deezer', 'dl-1', {
        'id': 'dl-1', 'filename': '111||A', 'username': 'deezer_dl',
        'state': 'InProgress, Downloading', 'progress': 50.0,
    })
    result = _run_async(client.get_all_downloads())
    assert len(result) == 1
    assert result[0].id == 'dl-1'
    assert result[0].username == 'deezer_dl'
