"""Manages mappings from source albums/tracks to specific target service IDs."""

import csv
import os
from dataclasses import dataclass
from pathlib import Path

from pushtunes.models.album import Album
from pushtunes.models.track import Track
from pushtunes.utils.logging import get_logger


@dataclass(frozen=True)
class AlbumMapping:
    """Maps a source album to a target album by service ID or metadata."""

    # Source album identifiers (from subsonic, csv, jellyfin)
    source_artist: str
    source_title: str

    # Target album identifiers (optional: use service ID if provided)
    spotify_id: str | None = None
    ytm_id: str | None = None

    # Target album metadata (optional: use if service ID not provided)
    spotify_artist: str | None = None
    spotify_title: str | None = None
    ytm_artist: str | None = None
    ytm_title: str | None = None


@dataclass(frozen=True)
class TrackMapping:
    """Maps a source track to a target track by service ID or metadata."""

    # Source track identifiers
    source_artist: str
    source_title: str

    # Target track identifiers (optional: use service ID if provided)
    spotify_id: str | None = None
    ytm_id: str | None = None

    # Target track metadata (optional: use if service ID not provided)
    spotify_artist: str | None = None
    spotify_title: str | None = None
    ytm_artist: str | None = None
    ytm_title: str | None = None


class MappingsManager:
    """Manages mappings from source albums/tracks to target service albums/tracks."""

    def __init__(self, mappings_file: str | Path | None = None):
        """Initialize the mappings manager.

        Args:
            mappings_file: Path to CSV file containing mappings (supports ~ for home directory)
        """
        self.log = get_logger(__name__)
        self.album_mappings: dict[tuple[str, str], AlbumMapping] = {}
        self.track_mappings: dict[tuple[str, str], TrackMapping] = {}

        if mappings_file:
            # Expand ~ to home directory
            expanded_path = os.path.expanduser(str(mappings_file))
            self._load_from_csv(Path(expanded_path))

    def _load_from_csv(self, csv_file: Path) -> None:
        """Load mappings from CSV file.

        CSV format:
        type,artist,title,spotify_id,ytm_id,spotify_artist,spotify_title,ytm_artist,ytm_title

        Where type is 'album' or 'track'.
        """
        if not csv_file.exists():
            self.log.warning(f"Mappings file not found: {csv_file}")
            return

        try:
            with open(csv_file, "r", encoding="utf-8") as f:
                reader = csv.DictReader(f)

                # Check if the CSV has a 'type' column, otherwise assume albums
                has_type_column = "type" in reader.fieldnames if reader.fieldnames else False

                for row_num, row in enumerate(reader, start=2):  # Start at 2 (header is line 1)
                    try:
                        # Determine the type of mapping
                        mapping_type = row.get("type", "album").strip().lower()

                        artist = row.get("artist", "").strip()
                        title = row.get("title", "").strip()

                        if not artist or not title:
                            self.log.warning(
                                f"Skipping row {row_num}: missing artist or title"
                            )
                            continue

                        # Extract target service IDs
                        spotify_id = row.get("spotify_id", "").strip() or None
                        ytm_id = row.get("ytm_id", "").strip() or None

                        # Extract target metadata
                        spotify_artist = row.get("spotify_artist", "").strip() or None
                        spotify_title = row.get("spotify_title", "").strip() or None
                        ytm_artist = row.get("ytm_artist", "").strip() or None
                        ytm_title = row.get("ytm_title", "").strip() or None

                        # At least one target must be specified
                        has_spotify = spotify_id or (spotify_artist and spotify_title)
                        has_ytm = ytm_id or (ytm_artist and ytm_title)

                        if not has_spotify and not has_ytm:
                            self.log.warning(
                                f"Skipping row {row_num}: no target specified for {artist} - {title}"
                            )
                            continue

                        # Create the appropriate mapping
                        if mapping_type == "track":
                            mapping = TrackMapping(
                                source_artist=artist,
                                source_title=title,
                                spotify_id=spotify_id,
                                ytm_id=ytm_id,
                                spotify_artist=spotify_artist,
                                spotify_title=spotify_title,
                                ytm_artist=ytm_artist,
                                ytm_title=ytm_title,
                            )
                            key = (artist.lower(), title.lower())
                            self.track_mappings[key] = mapping
                            self.log.debug(f"Loaded track mapping: {artist} - {title}")
                        else:  # Default to album
                            mapping = AlbumMapping(
                                source_artist=artist,
                                source_title=title,
                                spotify_id=spotify_id,
                                ytm_id=ytm_id,
                                spotify_artist=spotify_artist,
                                spotify_title=spotify_title,
                                ytm_artist=ytm_artist,
                                ytm_title=ytm_title,
                            )
                            key = (artist.lower(), title.lower())
                            self.album_mappings[key] = mapping
                            self.log.debug(f"Loaded album mapping: {artist} - {title}")

                    except Exception as e:
                        self.log.error(f"Error parsing row {row_num}: {e}")
                        continue

            self.log.info(
                f"Loaded {len(self.album_mappings)} album mappings and "
                f"{len(self.track_mappings)} track mappings from {csv_file}"
            )

        except Exception as e:
            self.log.error(f"Failed to load mappings from {csv_file}: {e}")

    def get_album_mapping(
        self, source_album: Album, service_name: str
    ) -> Album | None:
        """Get the target album for a source album on a specific service.

        Args:
            source_album: The source album to map
            service_name: The target service name ('spotify' or 'ytm')

        Returns:
            Album object with service_id or metadata for the target, or None if no mapping
        """
        # Look up mapping by artist and title (case-insensitive)
        key = (source_album.artist.lower(), source_album.title.lower())
        mapping = self.album_mappings.get(key)

        if not mapping:
            return None

        # Create the target album based on the service
        if service_name == "spotify":
            if mapping.spotify_id:
                # Use service ID directly
                return Album(
                    title=source_album.title,
                    artists=source_album.artists,
                    year=source_album.year,
                    service_id=mapping.spotify_id,
                    service_name="spotify",
                )
            elif mapping.spotify_artist and mapping.spotify_title:
                # Use metadata for search
                return Album(
                    title=mapping.spotify_title,
                    artists=[mapping.spotify_artist],
                    year=source_album.year,
                    service_name="spotify",
                )
        elif service_name == "ytm":
            if mapping.ytm_id:
                # Use service ID directly
                return Album(
                    title=source_album.title,
                    artists=source_album.artists,
                    year=source_album.year,
                    service_id=mapping.ytm_id,
                    service_name="ytm",
                )
            elif mapping.ytm_artist and mapping.ytm_title:
                # Use metadata for search
                return Album(
                    title=mapping.ytm_title,
                    artists=[mapping.ytm_artist],
                    year=source_album.year,
                    service_name="ytm",
                )

        return None

    def get_track_mapping(
        self, source_track: Track, service_name: str
    ) -> Track | None:
        """Get the target track for a source track on a specific service.

        Args:
            source_track: The source track to map
            service_name: The target service name ('spotify' or 'ytm')

        Returns:
            Track object with service_id or metadata for the target, or None if no mapping
        """
        # Look up mapping by artist and title (case-insensitive)
        key = (source_track.artist.lower(), source_track.title.lower())
        mapping = self.track_mappings.get(key)

        if not mapping:
            return None

        # Create the target track based on the service
        if service_name == "spotify":
            if mapping.spotify_id:
                # Use service ID directly
                return Track(
                    title=source_track.title,
                    artists=source_track.artists,
                    album=source_track.album,
                    year=source_track.year,
                    service_id=mapping.spotify_id,
                    service_name="spotify",
                )
            elif mapping.spotify_artist and mapping.spotify_title:
                # Use metadata for search
                return Track(
                    title=mapping.spotify_title,
                    artists=[mapping.spotify_artist],
                    album=source_track.album,
                    year=source_track.year,
                    service_name="spotify",
                )
        elif service_name == "ytm":
            if mapping.ytm_id:
                # Use service ID directly
                return Track(
                    title=source_track.title,
                    artists=source_track.artists,
                    album=source_track.album,
                    year=source_track.year,
                    service_id=mapping.ytm_id,
                    service_name="ytm",
                )
            elif mapping.ytm_artist and mapping.ytm_title:
                # Use metadata for search
                return Track(
                    title=mapping.ytm_title,
                    artists=[mapping.ytm_artist],
                    album=source_track.album,
                    year=source_track.year,
                    service_name="ytm",
                )

        return None
