"""Track redownload endpoint — lifted from web_server.py.

Body is byte-identical to the original. The ``spotify_client`` proxy
+ helper shims for the iTunes/Deezer registry clients let the body
resolve its original names; ``_resolve_library_file_path``,
``_attempt_download_with_candidates``, and ``missing_download_executor``
are injected via init() because they live in web_server.py.
"""
import logging
import time

from flask import jsonify, request

from core.runtime_state import (
    download_batches,
    download_tasks,
    tasks_lock,
)
from core.metadata.registry import (
    get_deezer_client,
    get_itunes_client,
    get_spotify_client,
)
from database.music_database import get_database

logger = logging.getLogger(__name__)


def _get_itunes_client():
    """Mirror of web_server._get_itunes_client — delegates to registry."""
    return get_itunes_client()


def _get_deezer_client():
    """Mirror of web_server._get_deezer_client — delegates to registry."""
    return get_deezer_client()


class _SpotifyClientProxy:
    """Resolves the global Spotify client lazily through core.metadata.registry."""

    def __getattr__(self, name):
        client = get_spotify_client()
        if client is None:
            raise AttributeError(name)
        return getattr(client, name)

    def __bool__(self):
        return get_spotify_client() is not None


spotify_client = _SpotifyClientProxy()


# Injected at runtime via init().
_resolve_library_file_path = None
_attempt_download_with_candidates = None
missing_download_executor = None


def init(resolve_library_file_path_fn, attempt_download_with_candidates_fn, executor):
    """Bind shared helpers from web_server."""
    global _resolve_library_file_path, _attempt_download_with_candidates
    global missing_download_executor
    _resolve_library_file_path = resolve_library_file_path_fn
    _attempt_download_with_candidates = attempt_download_with_candidates_fn
    missing_download_executor = executor


def redownload_start(track_id):
    """Start downloading a specific track from a selected source to replace the current file."""
    try:
        data = request.get_json()
        metadata = data.get('metadata', {})
        candidate = data.get('candidate', {})
        delete_old = data.get('delete_old_file', True)

        if not candidate.get('username') or not candidate.get('filename'):
            return jsonify({"success": False, "error": "candidate with username and filename required"}), 400

        # Get current track info for old file path
        database = get_database()
        conn = database._get_connection()
        cursor = conn.cursor()
        cursor.execute("SELECT file_path FROM tracks WHERE id = ?", (track_id,))
        row = cursor.fetchone()
        conn.close()

        old_file_path = None
        if row and row['file_path'] and delete_old:
            old_file_path = _resolve_library_file_path(row['file_path'])

        task_id = f"redownload_{track_id}_{int(time.time())}"
        batch_id = f"redownload_batch_{track_id}"

        # Fetch full track details from the metadata source for pipeline parity
        # This gives us track_number, disc_number, full album data
        meta_source = metadata.get('_source', '')
        meta_id = metadata.get('id', '')
        full_track_details = None
        full_album_data = None

        if meta_id:
            try:
                if meta_source == 'spotify' and spotify_client and spotify_client.is_authenticated():
                    full_track_details = spotify_client.get_track_details(meta_id)
                    if full_track_details and full_track_details.get('album', {}).get('id'):
                        full_album_data = spotify_client.get_album(full_track_details['album']['id'])
                elif meta_source == 'itunes':
                    _it = _get_itunes_client()
                    results = _it._lookup(id=meta_id, entity='song')
                    if results:
                        for r in results:
                            if r.get('wrapperType') == 'track':
                                full_track_details = r
                                break
                elif meta_source == 'deezer':
                    _dz = _get_deezer_client()
                    full_track_details = _dz._api_get(f'track/{meta_id}')
            except Exception as e:
                logger.debug(f"[Redownload] Could not fetch full track details: {e}")

        # Build track data with full metadata for pipeline parity
        track_number = None
        disc_number = 1
        album_data = {'name': metadata.get('album', '')}

        if full_track_details:
            if meta_source == 'spotify':
                track_number = full_track_details.get('track_number')
                disc_number = full_track_details.get('disc_number', 1)
                album_raw = full_track_details.get('album', {})
                if album_raw:
                    album_images = album_raw.get('images', [])
                    album_data = {
                        'id': album_raw.get('id', ''),
                        'name': album_raw.get('name', metadata.get('album', '')),
                        'release_date': album_raw.get('release_date', ''),
                        'album_type': album_raw.get('album_type', 'album'),
                        'total_tracks': album_raw.get('total_tracks', 0),
                        'images': album_images,
                        'image_url': album_images[0]['url'] if album_images else '',
                    }
            elif meta_source == 'itunes':
                track_number = full_track_details.get('trackNumber')
                disc_number = full_track_details.get('discNumber', 1)
            elif meta_source == 'deezer':
                track_number = full_track_details.get('track_position')
                disc_number = full_track_details.get('disk_number', 1)

        track_data = {
            'id': meta_id,
            'name': metadata.get('name', ''),
            'artists': [{'name': metadata.get('artist', '')}],
            'album': album_data,
            'duration_ms': metadata.get('duration_ms', 0),
            'track_number': track_number,
            'disc_number': disc_number,
            '_is_explicit_album_download': bool(full_album_data or (album_data.get('id'))),
        }

        # Build explicit context if we have full album data
        if full_album_data or album_data.get('id'):
            track_data['_explicit_album_context'] = full_album_data if isinstance(full_album_data, dict) else album_data
            track_data['_explicit_artist_context'] = {'name': metadata.get('artist', ''), 'id': '', 'genres': []}

        # Create batch
        with tasks_lock:
            download_batches[batch_id] = {
                'queue': [task_id],
                'queue_index': 1,  # Already past the first (only) item
                'active_count': 1,  # One worker is about to start
                'max_concurrent': 1,
                'playlist_id': f'redownload_{track_id}',
                'playlist_name': f"Redownload: {metadata.get('artist', '')} - {metadata.get('name', '')}",
                'phase': 'downloading',
                'total_tracks': 1,
                'completed_count': 0,
                'failed_count': 0,
                'cancelled_tracks': set(),
                'permanently_failed_tracks': [],
                'force_download': True,
                'auto_initiated': False,
            }

            download_tasks[task_id] = {
                'status': 'queued',
                'track_info': track_data,
                'playlist_id': f'redownload_{track_id}',
                'batch_id': batch_id,
                'track_index': 0,
                'download_id': None,
                'username': None,
                'filename': None,
                'retry_count': 0,
                'cached_candidates': [],
                'used_sources': set(),
                'status_change_time': time.time(),
                'metadata_enhanced': False,
                'error_message': None,
                '_redownload_context': {
                    'library_track_id': track_id,
                    'old_file_path': old_file_path,
                    'delete_old_file': delete_old,
                },
            }

        # Build a TrackResult-like candidate and submit to download
        def _run_redownload():
            try:
                from core.download_plugins.types import TrackResult
                from core.itunes_client import Track as MetaTrack
                tr = TrackResult(
                    username=candidate['username'],
                    filename=candidate['filename'],
                    size=candidate.get('size', 0),
                    bitrate=candidate.get('bitrate', 0),
                    duration=candidate.get('duration', 0),
                    quality=candidate.get('quality', ''),
                    free_upload_slots=candidate.get('free_upload_slots', 0),
                    upload_speed=candidate.get('upload_speed', 0),
                    queue_length=candidate.get('queue_length', 0),
                )
                tr.artist = metadata.get('artist', '')
                tr.title = metadata.get('name', '')
                tr.album = metadata.get('album', '')
                tr.confidence = candidate.get('confidence', 1.0)

                # Build a proper Track object (not a dict) — _attempt_download_with_candidates
                # accesses track.artists, track.album etc. as attributes
                artist_name = metadata.get('artist', '')
                track_obj = MetaTrack(
                    id=metadata.get('id', ''),
                    name=metadata.get('name', ''),
                    artists=[artist_name] if artist_name else ['Unknown'],
                    album=metadata.get('album', ''),
                    duration_ms=metadata.get('duration_ms', 0),
                    popularity=0,
                )

                _attempt_download_with_candidates(task_id, [tr], track_obj, batch_id)
            except Exception as e:
                logger.error(f"Redownload failed: {e}", exc_info=True)
                with tasks_lock:
                    if task_id in download_tasks:
                        download_tasks[task_id]['status'] = 'failed'
                        download_tasks[task_id]['error_message'] = str(e)

        missing_download_executor.submit(_run_redownload)

        return jsonify({
            "success": True,
            "task_id": task_id,
            "batch_id": batch_id,
            "message": "Redownload started",
        })
    except Exception as e:
        logger.error(f"Error starting redownload: {e}", exc_info=True)
        return jsonify({"success": False, "error": str(e)}), 500
