using YandexMusic.API.Common;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Landing;
using YandexMusic.API.Models.Landing.Entity.Entities;
using YandexMusic.API.Models.Playlist;
using YandexMusic.API.Models.Track;
using YandexMusic.API.Requests.Playlist;

namespace YandexMusic.API;

/// <summary>API для взаимодействия с плейлистами.</summary>
public class YPlaylistAPI : YCommonAPI
{
    /// <summary>Инициализирует новый экземпляр API плейлистов.</summary>
    /// <param name="yandex">Экземпляр основного API.</param>
    public YPlaylistAPI(YandexMusicApi yandex) : base(yandex) { }

    /// <summary>Получает список персональных плейлистов с главной страницы.</summary>
    /// <param name="storage">Хранилище авторизации.</param>
    /// <returns>Список ответов с плейлистами.</returns>
    public async Task<List<YResponse<YPlaylist>>> GetPersonalPlaylistsAsync(AuthStorage storage)
    {
        var landing = await api.Landing.GetAsync(storage, YLandingBlockType.PersonalPlaylists);
        var block = landing.Result?.Blocks?.FirstOrDefault(b => b.Type == YLandingBlockType.PersonalPlaylists);
        if (block?.Entities == null)
            return new List<YResponse<YPlaylist>>();

        var tasks = block.Entities
            .OfType<YLandingEntityPersonalPlaylist>()
            .Select(e => api.Playlist.GetAsync(storage, e.Data?.Data));

        return new List<YResponse<YPlaylist>>(await Task.WhenAll(tasks));
    }

    /// <summary>Получает избранные плейлисты.</summary>
    public Task<YResponse<List<YPlaylist>>> FavoritesAsync(AuthStorage storage)
        => new YGetPlaylistFavoritesBuilder(api, storage).Build(null!).GetResponseAsync();

    /// <summary>Получает плейлист дня.</summary>
    public Task<YResponse<YPlaylist>> OfTheDayAsync(AuthStorage storage)
        => GetPersonalPlaylistAsync(storage, YGeneratedPlaylistType.PlaylistOfTheDay);

    /// <summary>Получает плейлист «Дежавю».</summary>
    public Task<YResponse<YPlaylist>> DejaVuAsync(AuthStorage storage)
        => GetPersonalPlaylistAsync(storage, YGeneratedPlaylistType.NeverHeard);

    /// <summary>Получает плейлист «Премьера».</summary>
    public Task<YResponse<YPlaylist>> PremiereAsync(AuthStorage storage)
        => GetPersonalPlaylistAsync(storage, YGeneratedPlaylistType.RecentTracks);

    /// <summary>Получает плейлист «Тайник».</summary>
    public Task<YResponse<YPlaylist>> MissedAsync(AuthStorage storage)
        => GetPersonalPlaylistAsync(storage, YGeneratedPlaylistType.MissedLikes);

    /// <summary>Получает плейлист «Кинопоиск».</summary>
    public Task<YResponse<YPlaylist>> KinopoiskAsync(AuthStorage storage)
        => GetPersonalPlaylistAsync(storage, YGeneratedPlaylistType.Kinopoisk);

    private async Task<YResponse<YPlaylist>> GetPersonalPlaylistAsync(AuthStorage storage, YGeneratedPlaylistType type)
    {
        var list = await GetPersonalPlaylistsAsync(storage);
        return list.FirstOrDefault(e => string.Equals(e.Result?.GeneratedPlaylistType, type.ToString(), StringComparison.CurrentCultureIgnoreCase))
            ?? throw new Exception($"Плейлист типа {type} не найден.");
    }

    /// <summary>Получает плейлист по идентификатору пользователя и типа.</summary>
    public Task<YResponse<YPlaylist>> GetAsync(AuthStorage storage, string user, string kind)
        => new YGetPlaylistBuilder(api, storage).Build((user, kind)).GetResponseAsync();

    /// <summary>Получает плейлист по UUID.</summary>
    public Task<YResponse<YPlaylist>> GetAsync(AuthStorage storage, string uuid)
        => new YGetPlaylistByUuidBuilder(api, storage).Build(uuid).GetResponseAsync();

    /// <summary>Получает несколько плейлистов по списку пар (пользователь, тип).</summary>
    public Task<YResponse<List<YPlaylist>>> GetAsync(AuthStorage storage, IEnumerable<(string user, string kind)> ids)
        => new YGetPlaylistsBuilder(api, storage).Build(ids).GetResponseAsync();

    /// <summary>Получает плейлист по объекту плейлиста (обновляет его треки).</summary>
    public Task<YResponse<YPlaylist>> GetAsync(AuthStorage storage, YPlaylist playlist)
        => new YGetPlaylistBuilder(api, storage).Build((playlist.Owner.Uid, playlist.Kind)).GetResponseAsync();

    /// <summary>Создаёт новый плейлист с заданным именем.</summary>
    public Task<YResponse<YPlaylist>> CreateAsync(AuthStorage storage, string name)
        => new YPlaylistCreateBuilder(api, storage).Build(name).GetResponseAsync();

    /// <summary>Переименовывает плейлист.</summary>
    public Task<YResponse<YPlaylist>> RenameAsync(AuthStorage storage, string kind, string name)
        => new YPlaylistRenameBuilder(api, storage).Build((kind, name)).GetResponseAsync();

    /// <summary>Переименовывает плейлист.</summary>
    public Task<YResponse<YPlaylist>> RenameAsync(AuthStorage storage, YPlaylist playlist, string name)
        => RenameAsync(storage, playlist.Kind, name);

    /// <summary>Удаляет плейлист.</summary>
    public async Task<bool> DeleteAsync(AuthStorage storage, string kind)
    {
        try
        {
            await new YPlaylistRemoveBuilder(api, storage).Build(kind).GetResponseAsync();
            return true;
        }
        catch (Exception ex)
        {
            // Логирование ошибки можно добавить через ILogger
            return false;
        }
    }

    /// <summary>Удаляет плейлист.</summary>
    public Task<bool> DeleteAsync(AuthStorage storage, YPlaylist playlist)
        => DeleteAsync(storage, playlist.Kind);

    /// <summary>Добавляет треки в начало плейлиста.</summary>
    public async Task<YResponse<YPlaylist>> InsertTracksAsync(AuthStorage storage, YPlaylist playlist, IEnumerable<YTrack> tracks)
    {
        var change = await ChangePlaylistAsync(storage, playlist, new List<YPlaylistChange>
        {
            new()
            {
                Operation = YPlaylistChangeType.Insert,
                At = 0,
                Tracks = tracks.Select(t => t.GetKey())
            }
        });
        return await GetAsync(storage, change.Result);
    }

    /// <summary>Удаляет треки из плейлиста.</summary>
    public Task<YResponse<YPlaylist>> DeleteTracksAsync(AuthStorage storage, YPlaylist playlist, IEnumerable<YTrack> tracks)
    {
        var distinctTracks = tracks.Distinct().ToList();
        var changes = distinctTracks
            .Select(t => playlist.Tracks?.FindIndex(ct => ct.Track?.GetKey() == t.GetKey()) ?? -1)
            .Where(i => i != -1)
            .Select(i => new YPlaylistChange
            {
                Operation = YPlaylistChangeType.Delete,
                From = i,
                To = i + 1,
                Tracks = new List<YTrackAlbumPair> { playlist.Tracks![i].Track!.GetKey() }
            })
            .ToList();

        return ChangePlaylistAsync(storage, playlist, changes);
    }

    private Task<YResponse<YPlaylist>> ChangePlaylistAsync(AuthStorage storage, YPlaylist playlist, IEnumerable<YPlaylistChange> changes)
        => new YPlaylistChangeBuilder(api, storage).Build((playlist, changes)).GetResponseAsync();
}