"""CSV utilities for importing and exporting song data."""

import csv
import os

from ..models.album import Album
from ..models.track import Track
from ..models.playlist import Playlist
from .artist_utils import parse_artist_string


def export_albums_to_csv(albums: list[Album], csv_file: str) -> None:
    """Export a list of albums to a CSV file.

    Args:
        albums: list of Album objects to export
        csv_file: Path to the output CSV file
    """
    with open(csv_file, "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f, quoting=csv.QUOTE_ALL)
        # Write header
        writer.writerow(["artist", "album", "year", "spotify_id", "ytm_id"])

        # Write album data
        for album in albums:
            # Extract service IDs if available
            spotify_id = album.service_id if album.service_name == "spotify" else ""
            ytm_id = album.service_id if album.service_name == "ytm" else ""

            writer.writerow([album.artist, album.title, album.year or "", spotify_id, ytm_id])


def import_albums_from_csv(csv_file: str) -> list[Album]:
    """Import albums from a CSV file with 'artist', 'album' fields.
    Supports multiple artist columns: 'artist', 'artist2', ..., 'artist10'.
    Also supports service IDs: 'spotify_id' and 'ytm_id' columns.

    Args:
        csv_file: Path to the input CSV file

    Returns:
        list of Album objects
    """
    albums = []
    with open(csv_file, "r", newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            artist_names = []
            # Check for artist2, artist3 etc. first
            for i in range(2, 11):
                additional_artist = row.get(f"artist{i}")
                if additional_artist:
                    artist_names.append(additional_artist)

            primary_artist = row.get("artist")
            if primary_artist:
                # If there were additional artist columns, we assume the user has
                # already split the artists and we just prepend the primary artist.
                # If not, we parse the primary artist string for separators.
                if artist_names:
                    artist_names.insert(0, primary_artist)
                else:
                    artist_names.extend(parse_artist_string(primary_artist))

            year_str = row.get("year")
            year = int(year_str) if year_str else None

            # Store both service IDs in extra_data
            extra_data = {}
            spotify_id = row.get("spotify_id", "").strip()
            ytm_id = row.get("ytm_id", "").strip()

            if spotify_id:
                extra_data["spotify_id"] = spotify_id
            if ytm_id:
                extra_data["ytm_id"] = ytm_id

            album = Album(
                artists=artist_names,
                title=row.get("album", ""),
                year=year,
                extra_data=extra_data if extra_data else None,
            )
            albums.append(album)
    return albums


def export_tracks_to_csv(tracks: list[Track], csv_file: str) -> None:
    """Export a list of tracks to a CSV file.

    Args:
        tracks: list of Track objects to export
        csv_file: Path to the output CSV file
    """
    with open(csv_file, "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f, quoting=csv.QUOTE_ALL)
        # Write header
        writer.writerow(["artist", "track", "album", "year", "spotify_id", "ytm_id"])

        # Write track data
        for track in tracks:
            # Extract service IDs if available
            spotify_id = track.service_id if track.service_name == "spotify" else ""
            ytm_id = track.service_id if track.service_name == "ytm" else ""

            writer.writerow([track.artist, track.title, track.album or "", track.year or "", spotify_id, ytm_id])


def import_tracks_from_csv(csv_file: str) -> list[Track]:
    """Import tracks from a CSV file with 'artist' and 'track' fields.
    Supports single artist per track (as per requirements).
    Also supports service IDs: 'spotify_id' and 'ytm_id' columns.

    Args:
        csv_file: Path to the input CSV file

    Returns:
        list of Track objects
    """
    tracks = []
    with open(csv_file, "r", newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            artist = row.get("artist", "")
            artist_list = [artist] if artist else []

            year_str = row.get("year")
            year = int(year_str) if year_str and year_str.isdigit() else None

            # Store both service IDs in extra_data
            extra_data = {}
            spotify_id = row.get("spotify_id", "").strip()
            ytm_id = row.get("ytm_id", "").strip()

            if spotify_id:
                extra_data["spotify_id"] = spotify_id
            if ytm_id:
                extra_data["ytm_id"] = ytm_id

            track = Track(
                artists=artist_list,
                title=row.get("track", "") or row.get("title", ""),
                album=row.get("album") or None,
                year=year,
                extra_data=extra_data if extra_data else None,
            )
            tracks.append(track)
    return tracks


def export_playlist_to_csv(playlist: Playlist, csv_file: str) -> None:
    """Export a playlist to a CSV file.

    Uses the same format as tracks (artist, track, album, year).

    Args:
        playlist: Playlist object to export
        csv_file: Path to the output CSV file
    """
    export_tracks_to_csv(playlist.tracks, csv_file)


def import_playlist_from_csv(csv_file: str, playlist_name: str | None = None) -> Playlist:
    """Import a playlist from a CSV file.

    Uses the same format as tracks (artist, track, album, year).

    Args:
        csv_file: Path to the input CSV file
        playlist_name: Name for the playlist (defaults to filename without extension)

    Returns:
        Playlist object
    """
    tracks = import_tracks_from_csv(csv_file)

    # Use filename (without extension) as playlist name if not provided
    if not playlist_name:
        import os
        playlist_name = os.path.splitext(os.path.basename(csv_file))[0]

    return Playlist(name=playlist_name, tracks=tracks)


def export_album_results_to_csv(results: list, status_filter: list[str], csv_file: str) -> int:
    """Export album results matching specified statuses to a CSV file.

    Args:
        results: List of AlbumResult objects
        status_filter: List of status names to include (e.g., ['not_found', 'similarity_too_low'])
        csv_file: Path to the output CSV file

    Returns:
        Number of albums exported
    """
    # Filter results by status
    filtered = [r for r in results if r.status.name in status_filter]

    if not filtered:
        return 0

    # Extract albums from results
    albums = [r.album for r in filtered]

    # Export using existing function
    export_albums_to_csv(albums, csv_file)

    return len(albums)


def export_track_results_to_csv(results: list, status_filter: list[str], csv_file: str) -> int:
    """Export track results matching specified statuses to a CSV file.

    Args:
        results: List of TrackResult objects
        status_filter: List of status names to include (e.g., ['not_found', 'similarity_too_low'])
        csv_file: Path to the output CSV file

    Returns:
        Number of tracks exported
    """
    # Filter results by status
    filtered = [r for r in results if r.status.name in status_filter]

    if not filtered:
        return 0

    # Extract tracks from results
    tracks = [r.track for r in filtered]

    # Export using existing function
    export_tracks_to_csv(tracks, csv_file)

    return len(tracks)


def _read_existing_mappings_csv(csv_file: str) -> tuple[dict[tuple, list], list, set[tuple]]:
    """Read existing mappings CSV file and return existing rows preserving all data.

    Args:
        csv_file: Path to the existing CSV file

    Returns:
        Tuple of (existing_mappings_dict, existing_rows_list, mapped_items_set)
        - existing_mappings_dict: Maps (type, artist_lower, title_lower) -> full row data
        - existing_rows_list: List of all existing rows (as lists) to preserve order
        - mapped_items_set: Set of keys for items that have at least one target field filled
    """
    existing_mappings = {}
    existing_rows = []
    mapped_items = set()

    if not os.path.exists(csv_file):
        return existing_mappings, existing_rows, mapped_items

    with open(csv_file, "r", newline="", encoding="utf-8") as f:
        reader = csv.reader(f)
        header = next(reader, None)  # Skip header

        if not header:
            return existing_mappings, existing_rows, mapped_items

        for row in reader:
            if len(row) >= 3:  # At minimum need type, artist, title
                item_type = row[0].lower()
                artist = row[1]
                title = row[2]

                # Create lookup key (case-insensitive)
                key = (item_type, artist.lower(), title.lower())
                existing_mappings[key] = row
                existing_rows.append(row)

                # Check if this item has any target fields filled in
                # Columns 3-8 are: spotify_id, ytm_id, spotify_artist, spotify_title, ytm_artist, ytm_title
                if len(row) >= 9:
                    target_fields = row[3:9]  # Get all 6 target fields
                    has_mapping = any(field and field.strip() for field in target_fields)
                    if has_mapping:
                        mapped_items.add(key)

    return existing_mappings, existing_rows, mapped_items


def export_album_results_to_mappings_csv(results: list, status_filter: list[str], csv_file: str) -> int:
    """Export album results matching specified statuses to a mappings CSV file.

    Creates a CSV file in the mappings format with empty target fields that the user can fill in.

    This function operates incrementally: if the target file already exists, it will:
    - Preserve all existing rows and their data (especially user-filled IDs and titles)
    - Only add NEW albums that aren't already in the file
    - Never delete or modify existing mappings

    This allows users to run export multiple times and gradually build up a mappings file
    without losing manual edits.

    Args:
        results: List of AlbumResult objects
        status_filter: List of status names to include (e.g., ['not_found'])
        csv_file: Path to the output CSV file

    Returns:
        Tuple of (new_count, already_unmapped_count):
        - new_count: Number of NEW albums added to the file
        - already_unmapped_count: Number of failed albums already in file with empty target fields
    """
    # Filter results by status
    filtered = [r for r in results if r.status.name in status_filter]

    # Read existing mappings to preserve user data
    existing_mappings, existing_rows, mapped_items = _read_existing_mappings_csv(csv_file)

    # Collect new rows to add and count items already in file
    new_rows = []
    already_in_file_unmapped = 0

    for result in filtered:
        # Create lookup key (case-insensitive)
        key = ("album", result.album.artist.lower(), result.album.title.lower())

        if key in mapped_items:
            # Already in file with filled target fields - skip completely
            continue
        elif key in existing_mappings:
            # In file but with empty target fields - count it but don't add duplicate
            already_in_file_unmapped += 1
        else:
            # Brand new item - add it
            new_rows.append([
                "album",
                result.album.artist,
                result.album.title,
                "",  # spotify_id
                "",  # ytm_id
                "",  # spotify_artist
                "",  # spotify_title
                "",  # ytm_artist
                ""   # ytm_title
            ])

    # If no existing rows and no new rows, nothing to write
    if not existing_rows and not new_rows:
        return (0, already_in_file_unmapped)

    # Write all rows (existing + new) to file
    with open(csv_file, "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f, quoting=csv.QUOTE_ALL)
        # Write header
        writer.writerow([
            "type", "artist", "title",
            "spotify_id", "ytm_id",
            "spotify_artist", "spotify_title",
            "ytm_artist", "ytm_title"
        ])

        # Write existing rows first (preserving user data)
        for row in existing_rows:
            writer.writerow(row)

        # Write new rows
        for row in new_rows:
            writer.writerow(row)

    return (len(new_rows), already_in_file_unmapped)


def export_track_results_to_mappings_csv(results: list, status_filter: list[str], csv_file: str) -> tuple[int, int]:
    """Export track results matching specified statuses to a mappings CSV file.

    Creates a CSV file in the mappings format with empty target fields that the user can fill in.

    This function operates incrementally: if the target file already exists, it will:
    - Preserve all existing rows and their data (especially user-filled IDs and titles)
    - Only add NEW tracks that aren't already in the file
    - Never delete or modify existing mappings

    This allows users to run export multiple times and gradually build up a mappings file
    without losing manual edits.

    Args:
        results: List of TrackResult objects
        status_filter: List of status names to include (e.g., ['not_found'])
        csv_file: Path to the output CSV file

    Returns:
        Tuple of (new_count, already_unmapped_count):
        - new_count: Number of NEW tracks added to the file
        - already_unmapped_count: Number of failed tracks already in file with empty target fields
    """
    # Filter results by status
    filtered = [r for r in results if r.status.name in status_filter]

    # Read existing mappings to preserve user data
    existing_mappings, existing_rows, mapped_items = _read_existing_mappings_csv(csv_file)

    # Collect new rows to add and count items already in file
    new_rows = []
    already_in_file_unmapped = 0

    for result in filtered:
        # Create lookup key (case-insensitive)
        key = ("track", result.track.artist.lower(), result.track.title.lower())

        if key in mapped_items:
            # Already in file with filled target fields - skip completely
            continue
        elif key in existing_mappings:
            # In file but with empty target fields - count it but don't add duplicate
            already_in_file_unmapped += 1
        else:
            # Brand new item - add it
            new_rows.append([
                "track",
                result.track.artist,
                result.track.title,
                "",  # spotify_id
                "",  # ytm_id
                "",  # spotify_artist
                "",  # spotify_title
                "",  # ytm_artist
                ""   # ytm_title
            ])

    # If no existing rows and no new rows, nothing to write
    if not existing_rows and not new_rows:
        return (0, already_in_file_unmapped)

    # Write all rows (existing + new) to file
    with open(csv_file, "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f, quoting=csv.QUOTE_ALL)
        # Write header
        writer.writerow([
            "type", "artist", "title",
            "spotify_id", "ytm_id",
            "spotify_artist", "spotify_title",
            "ytm_artist", "ytm_title"
        ])

        # Write existing rows first (preserving user data)
        for row in existing_rows:
            writer.writerow(row)

        # Write new rows
        for row in new_rows:
            writer.writerow(row)

    return (len(new_rows), already_in_file_unmapped)
