"""Service connection test — lifted from web_server.py.

The function body is byte-identical to the original. download_orchestrator,
qobuz_enrichment_worker, hydrabase_client, docker_resolve_url, and
docker_resolve_path are injected at runtime because they live in
web_server.py and are constructed there.
"""
import logging
import os

import requests

from config.settings import config_manager
from core.jellyfin_client import JellyfinClient
from core.metadata.registry import get_primary_source
from core.plex_client import PlexClient
from core.spotify_client import SpotifyClient
from core.tidal_client import TidalClient
from utils.async_helpers import run_async

logger = logging.getLogger(__name__)


def _get_metadata_fallback_source():
    """Mirror of web_server._get_metadata_fallback_source — delegates to registry."""
    return get_primary_source()


# Injected at runtime via init().
download_orchestrator = None
qobuz_enrichment_worker = None
hydrabase_client = None
docker_resolve_url = None
docker_resolve_path = None


def init(
    download_orchestrator_obj,
    qobuz_worker,
    hydrabase_client_obj,
    docker_resolve_url_fn,
    docker_resolve_path_fn,
):
    """Bind web_server-side helpers/globals so the lifted body can resolve them."""
    global download_orchestrator, qobuz_enrichment_worker, hydrabase_client
    global docker_resolve_url, docker_resolve_path
    download_orchestrator = download_orchestrator_obj
    qobuz_enrichment_worker = qobuz_worker
    hydrabase_client = hydrabase_client_obj
    docker_resolve_url = docker_resolve_url_fn
    docker_resolve_path = docker_resolve_path_fn


def run_service_test(service, test_config):
    """
    Performs the actual connection test for a given service.
    This logic is adapted from your ServiceTestThread.
    It temporarily modifies the config, runs the test, then restores the config.
    """
    original_config = {}
    try:
        # 1. Save original config for the specific service
        original_config = config_manager.get(service, {})

        # 2. Temporarily set the new config for the test (with Docker URL resolution)
        for key, value in test_config.items():
            # Apply Docker URL resolution for URL/URI fields
            if isinstance(value, str) and ('url' in key.lower() or 'uri' in key.lower()):
                value = docker_resolve_url(value)
            config_manager.set(f"{service}.{key}", value)

        # 3. Run the test with the temporary config
        if service == "spotify":
            temp_client = SpotifyClient()
            
            # Check if Spotify credentials are configured
            spotify_config = config_manager.get('spotify', {})
            spotify_configured = bool(spotify_config.get('client_id') and spotify_config.get('client_secret'))
            
            if temp_client.is_authenticated():
                 # Determine which source is active
                 if temp_client.is_spotify_authenticated():
                     return True, "Spotify connection successful!"
                 else:
                     # Using fallback metadata source
                     fb_src = _get_metadata_fallback_source()
                     fallback_name = 'Deezer' if fb_src == 'deezer' else 'Discogs' if fb_src == 'discogs' else 'iTunes'
                     if spotify_configured:
                         return True, f"{fallback_name} connection successful! (Spotify configured but not authenticated)"
                     else:
                         return True, f"{fallback_name} connection successful! (Spotify not configured)"
            else:
                 return False, "Music service authentication failed. Check credentials and complete OAuth flow in browser if prompted."
        elif service == "tidal":
            temp_client = TidalClient()
            if temp_client.is_authenticated():
                user_info = temp_client.get_user_info()
                username = user_info.get('display_name', 'Tidal User') if user_info else 'Tidal User'
                return True, f"Tidal connection successful! Connected as: {username}"
            else:
                return False, "Tidal authentication failed. Please use the 'Authenticate' button and complete the flow in your browser."
        elif service == "plex":
            temp_client = PlexClient()
            if temp_client.is_connected():
                return True, f"Successfully connected to Plex server: {temp_client.server.friendlyName}"
            else:
                return False, "Could not connect to Plex. Check URL and Token."
        elif service == "jellyfin":
            temp_client = JellyfinClient()
            if temp_client.is_connected():
                # FIX: Check if server_info exists before accessing it.
                server_name = "Unknown Server"
                if hasattr(temp_client, 'server_info') and temp_client.server_info:
                    server_name = temp_client.server_info.get('ServerName', 'Unknown Server')
                return True, f"Successfully connected to Jellyfin server: {server_name}"
            else:
                return False, "Could not connect to Jellyfin. Check URL and API Key."
        elif service == "navidrome":
            # Test Navidrome connection using Subsonic API
            base_url = test_config.get('base_url', '')
            username = test_config.get('username', '')
            password = test_config.get('password', '')

            if not all([base_url, username, password]):
                return False, "Missing Navidrome URL, username, or password."

            try:
                import hashlib
                import random
                import string

                # Generate salt and token for Subsonic API authentication
                salt = ''.join(random.choices(string.ascii_letters + string.digits, k=6))
                token = hashlib.md5((password + salt).encode()).hexdigest()

                # Test ping endpoint
                url = f"{base_url.rstrip('/')}/rest/ping"
                response = requests.get(url, params={
                    'u': username,
                    't': token,
                    's': salt,
                    'v': '1.16.1',
                    'c': 'soulsync',
                    'f': 'json'
                }, timeout=5)

                if response.status_code == 200:
                    data = response.json()
                    if data.get('subsonic-response', {}).get('status') == 'ok':
                        server_version = data.get('subsonic-response', {}).get('version', 'Unknown')
                        return True, f"Successfully connected to Navidrome server (v{server_version})"
                    else:
                        error = data.get('subsonic-response', {}).get('error', {})
                        return False, f"Navidrome authentication failed: {error.get('message', 'Unknown error')}"
                else:
                    return False, f"Could not connect to Navidrome server (HTTP {response.status_code})"

            except Exception as e:
                return False, f"Navidrome connection error: {str(e)}"
        elif service == "soulsync":
            transfer_path = docker_resolve_path(config_manager.get('soulseek.transfer_path', './Transfer'))
            if os.path.isdir(transfer_path):
                # Quick check — count a few audio files to confirm it's a music folder
                audio_exts = {'.mp3', '.flac', '.ogg', '.opus', '.m4a', '.aac', '.wav'}
                count = 0
                found_enough = False
                for _root, _dirs, files in os.walk(transfer_path):
                    for f in files:
                        if os.path.splitext(f)[1].lower() in audio_exts:
                            count += 1
                            if count >= 10:
                                found_enough = True
                                break
                    if found_enough:
                        break
                return True, f"SoulSync standalone ready! Output folder: {transfer_path}" + (f" ({count}+ audio files)" if count > 0 else " (empty)")
            else:
                return False, f"Output folder not found: {transfer_path}"
        elif service == "soulseek":
            if download_orchestrator is None:
                return False, "Download orchestrator failed to initialize. Check server logs for startup errors."

            # Test the orchestrator's configured download source (not just Soulseek)
            download_mode = config_manager.get('download_source.mode', 'hybrid')

            if run_async(download_orchestrator.check_connection()):
                # Success message based on active mode
                mode_messages = {
                    'soulseek': "Successfully connected to Soulseek network via slskd.",
                    'youtube': "YouTube download source ready.",
                    'tidal': "Tidal download source ready.",
                    'qobuz': "Qobuz download source ready.",
                    'hifi': "HiFi download source ready.",
                    'deezer_dl': "Deezer download source ready.",
                    'amazon': "Amazon download source ready.",
                    'lidarr': "Lidarr download source ready.",
                    'soundcloud': "SoundCloud download source ready.",
                    'torrent': "Torrent download source ready.",
                    'usenet': "Usenet download source ready.",
                    'hybrid': "Download sources ready (Hybrid mode)."
                }
                message = mode_messages.get(download_mode, "Download source connected.")
                return True, message
            else:
                # Failure message based on active mode
                mode_errors = {
                    'soulseek': "slskd is not connected to the Soulseek network. Check slskd status and credentials.",
                    'youtube': "YouTube download source not available.",
                    'tidal': "Tidal download source not available. Check authentication.",
                    'qobuz': "Qobuz download source not available. Check authentication.",
                    'hifi': "HiFi download source not available. Public API instances may be down.",
                    'deezer_dl': "Deezer download source not available. Check authentication.",
                    'amazon': "Amazon download source not available.",
                    'lidarr': "Lidarr download source not available. Check Lidarr URL and API key.",
                    'soundcloud': "SoundCloud download source not available.",
                    'torrent': "Torrent download source not available. Check Prowlarr and torrent client settings.",
                    'usenet': "Usenet download source not available. Check Prowlarr and usenet client settings.",
                    'hybrid': "Could not connect to download sources. Check configuration."
                }
                error = mode_errors.get(download_mode, "Download source connection failed.")
                return False, error
        elif service == "listenbrainz":
            token = test_config.get('token', '')

            if not token:
                return False, "Missing ListenBrainz user token."

            try:
                # Test ListenBrainz API by validating the token
                custom_base = test_config.get('base_url', '').rstrip('/')
                if custom_base:
                    if not custom_base.endswith('/1'):
                        custom_base += '/1'
                    lb_api_base = custom_base
                else:
                    lb_api_base = "https://api.listenbrainz.org/1"
                url = f"{lb_api_base}/validate-token"
                headers = {
                    'Authorization': f'Token {token}'
                }
                response = requests.get(url, headers=headers, timeout=5)

                if response.status_code == 200:
                    data = response.json()
                    if data.get('valid'):
                        username = data.get('user_name', 'Unknown')
                        return True, f"Successfully connected to ListenBrainz! Connected as: {username}"
                    else:
                        return False, "Invalid ListenBrainz token."
                elif response.status_code == 401:
                    return False, "Invalid ListenBrainz token (unauthorized)."
                else:
                    return False, f"Could not connect to ListenBrainz (HTTP {response.status_code})"

            except Exception as e:
                return False, f"ListenBrainz connection error: {str(e)}"
        elif service == "acoustid":
            api_key = test_config.get('api_key', '')

            if not api_key:
                return False, "Missing AcoustID API key."

            try:
                from core.acoustid_client import AcoustIDClient, CHROMAPRINT_AVAILABLE, ACOUSTID_AVAILABLE, FPCALC_PATH

                if not ACOUSTID_AVAILABLE:
                    return False, "pyacoustid library not installed. Run: pip install pyacoustid"

                client = AcoustIDClient()

                # Override the cached API key with the test config key
                client._api_key = api_key

                # Check chromaprint/fpcalc availability
                if CHROMAPRINT_AVAILABLE and FPCALC_PATH:
                    fingerprint_status = f"fpcalc ready: {FPCALC_PATH}"
                elif CHROMAPRINT_AVAILABLE:
                    fingerprint_status = "Fingerprint backend available"
                else:
                    fingerprint_status = "fpcalc not found (will auto-download on first use)"

                # Validate API key with test request
                success, message = client.test_api_key()
                if success:
                    return True, f"AcoustID API key is valid! {fingerprint_status}"
                else:
                    return False, f"{message}. {fingerprint_status}"
            except Exception as e:
                return False, f"AcoustID test error: {str(e)}"
        elif service == "lastfm":
            api_key = test_config.get('api_key', '')

            if not api_key:
                return False, "Missing Last.fm API key."

            try:
                from core.lastfm_client import LastFMClient
                client = LastFMClient(api_key=api_key)
                if client.validate_api_key():
                    return True, "Successfully connected to Last.fm!"
                else:
                    return False, "Invalid Last.fm API key."
            except Exception as e:
                return False, f"Last.fm connection error: {str(e)}"
        elif service == "genius":
            access_token = test_config.get('access_token', '')

            if not access_token:
                return False, "Missing Genius access token."

            try:
                from core.genius_client import GeniusClient
                client = GeniusClient(access_token=access_token)
                if client.validate_token():
                    return True, "Successfully connected to Genius!"
                else:
                    return False, "Invalid Genius access token."
            except Exception as e:
                return False, f"Genius connection error: {str(e)}"
        elif service == "usenet_client":
            client_type = (config_manager.get('usenet_client.type', '') or '').strip().lower()
            url = config_manager.get('usenet_client.url', '')
            if not url:
                return False, "Usenet client URL is required."
            if not client_type:
                return False, "Pick a usenet client (SABnzbd or NZBGet)."
            try:
                from core.usenet_clients import adapter_for_type as _usenet_adapter_for_type
                adapter = _usenet_adapter_for_type(client_type)
                if adapter is None:
                    return False, f"Unknown usenet client type: {client_type}"
                if not adapter.is_configured():
                    if client_type == "sabnzbd":
                        return False, "SABnzbd needs both URL and API key."
                    return False, "NZBGet needs URL, username, and password."
                if run_async(adapter.check_connection()):
                    return True, f"Connected to {client_type}"
                return False, f"{client_type} probe failed — check URL, credentials, and that the client is running."
            except Exception as e:
                return False, f"Usenet client connection error: {str(e)}"
        elif service == "torrent_client":
            client_type = (config_manager.get('torrent_client.type', '') or '').strip().lower()
            url = config_manager.get('torrent_client.url', '')
            if not url:
                return False, "Torrent client URL is required."
            if not client_type:
                return False, "Pick a torrent client (qBittorrent, Transmission, or Deluge)."
            try:
                from core.torrent_clients import adapter_for_type
                adapter = adapter_for_type(client_type)
                if adapter is None:
                    return False, f"Unknown torrent client type: {client_type}"
                if not adapter.is_configured():
                    return False, "Torrent client missing required credentials."
                if run_async(adapter.check_connection()):
                    return True, f"Connected to {client_type}"
                return False, f"{client_type} probe failed — check URL, credentials, and that the client is running."
            except Exception as e:
                return False, f"Torrent client connection error: {str(e)}"
        elif service == "prowlarr":
            url = config_manager.get('prowlarr.url', '')
            api_key = config_manager.get('prowlarr.api_key', '')
            if not url or not api_key:
                return False, "Prowlarr URL and API key are required."
            try:
                import requests as _req
                resp = _req.get(f"{url.rstrip('/')}/api/v1/system/status",
                                headers={'X-Api-Key': api_key}, timeout=10)
                if resp.ok:
                    version = resp.json().get('version', '?')
                    return True, f"Connected to Prowlarr v{version}"
                return False, f"Prowlarr returned HTTP {resp.status_code}"
            except Exception as e:
                return False, f"Prowlarr connection error: {str(e)}"
        elif service == "lidarr" or service == "lidarr_download":
            url = config_manager.get('lidarr_download.url', '')
            api_key = config_manager.get('lidarr_download.api_key', '')
            if not url or not api_key:
                return False, "Lidarr URL and API key are required."
            try:
                import requests as _req
                resp = _req.get(f"{url.rstrip('/')}/api/v1/system/status",
                                headers={'X-Api-Key': api_key}, timeout=10)
                if resp.ok:
                    version = resp.json().get('version', '?')
                    return True, f"Connected to Lidarr v{version}"
                return False, f"Lidarr returned HTTP {resp.status_code}"
            except Exception as e:
                return False, f"Lidarr connection error: {str(e)}"
        elif service == "itunes":
            # Public API — just confirm we can reach it with a cheap search
            try:
                storefront = config_manager.get('itunes.storefront', 'US') or 'US'
                resp = requests.get(
                    'https://itunes.apple.com/search',
                    params={'term': 'beatles', 'limit': 1, 'country': storefront, 'media': 'music'},
                    timeout=5,
                )
                if resp.ok and resp.json().get('resultCount', 0) >= 0:
                    return True, f"iTunes Search API reachable (storefront: {storefront})"
                return False, f"iTunes returned HTTP {resp.status_code}"
            except Exception as e:
                return False, f"iTunes connection error: {str(e)}"
        elif service == "deezer":
            # Public API — anon search works without credentials
            try:
                resp = requests.get(
                    'https://api.deezer.com/search/artist',
                    params={'q': 'beatles', 'limit': 1},
                    timeout=5,
                )
                if resp.ok and isinstance(resp.json(), dict):
                    return True, "Deezer Public API reachable"
                return False, f"Deezer returned HTTP {resp.status_code}"
            except Exception as e:
                return False, f"Deezer connection error: {str(e)}"
        elif service == "discogs":
            token = test_config.get('token', '') or config_manager.get('discogs.token', '')
            if not token:
                return False, "Missing Discogs personal token."
            try:
                resp = requests.get(
                    'https://api.discogs.com/database/search',
                    params={'q': 'beatles', 'per_page': 1},
                    headers={'Authorization': f'Discogs token={token}', 'User-Agent': 'SoulSync/1.0'},
                    timeout=10,
                )
                if resp.ok:
                    return True, "Discogs API reachable with provided token"
                if resp.status_code == 401:
                    return False, "Discogs token rejected (HTTP 401)"
                return False, f"Discogs returned HTTP {resp.status_code}"
            except Exception as e:
                return False, f"Discogs connection error: {str(e)}"
        elif service == "qobuz":
            try:
                if qobuz_enrichment_worker and qobuz_enrichment_worker.client and qobuz_enrichment_worker.client.is_authenticated():
                    return True, "Qobuz client authenticated"
                return False, "Qobuz not authenticated. Provide email/password or user auth token."
            except Exception as e:
                return False, f"Qobuz connection error: {str(e)}"
        elif service == "hydrabase":
            try:
                if hydrabase_client and hydrabase_client.is_connected():
                    return True, "Hydrabase connected"
                return False, "Hydrabase not connected. Configure URL + API key and click Connect."
            except Exception as e:
                return False, f"Hydrabase connection error: {str(e)}"
        elif service == "musicbrainz":
            try:
                from core.metadata.registry import get_musicbrainz_client
                mb = get_musicbrainz_client()
                results = mb.search_artists("radiohead", limit=1)
                if results:
                    return True, "MusicBrainz reachable"
                return False, "MusicBrainz returned no results — may be rate-limited or unreachable."
            except Exception as e:
                return False, f"MusicBrainz connection error: {str(e)}"
        elif service == "soundcloud":
            # Anonymous SoundCloud has no auth, so "test" really means
            # "is yt-dlp installed and can it reach SoundCloud right now."
            # This mirrors the /api/soundcloud/status check.
            try:
                from core.soundcloud_client import SoundcloudClient
                sc = SoundcloudClient()
                if not sc.is_available():
                    return False, "SoundCloud unavailable — yt-dlp not installed."
                # Run a tiny live probe via asyncio so the dashboard test
                # gives a meaningful pass/fail.
                import asyncio
                reachable = asyncio.new_event_loop().run_until_complete(sc.check_connection())
                if reachable:
                    return True, "SoundCloud reachable (anonymous)"
                return False, "SoundCloud unreachable — search probe failed. Try again."
            except Exception as e:
                return False, f"SoundCloud connection error: {str(e)}"
        return False, "Unknown service."
    except AttributeError as e:
        # This specifically catches the error you reported for Jellyfin
        if "'JellyfinClient' object has no attribute 'server_info'" in str(e):
            return False, "Connection failed. Please check your Jellyfin URL and API Key."
        else:
            return False, f"An unexpected error occurred: {e}"
    except Exception as e:
        import traceback
        traceback.print_exc()
        return False, str(e)
    finally:
        # 4. CRITICAL: Restore the original config
        if original_config:
            for key, value in original_config.items():
                config_manager.set(f"{service}.{key}", value)
            logger.debug(f"Restored original config for '{service}' after test.")
