using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;

using FuzzySharp;
using FuzzySharp.Extractor;

using HomeAssistantGenerated;

using NetDaemon.AppModel;
using NetDaemon.HassModel;
using NetDaemon.HassModel.Integration;

using NetDaemonConfig.Apps.Spotify.Types;


namespace NetDaemonConfig.Apps.Spotify.PlayAlbum
{
    public record PlayAlbumData(string? artist, string? album);

    [NetDaemonApp]
    public class PlayAlbum
    {
        private readonly CultureInfo _cultureInfo = new("fr-CA", false);

        // Snake-case json options
        private readonly JsonSerializerOptions _jsonOptions = new()
        {
            PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
        };

        private async Task CallBack(PlayAlbumData e, Services services)
        {
            string uri;

            if (e.artist is not null && e.artist != "")
            {
                SpotifyplusSearchArtistsResponse? artistResult = (
                    await services.Spotifyplus.SearchArtistsAsync(
                        criteria: e?.artist ??
                            throw new TargetException($"The artist {e?.artist} could not be found."),
                        limitTotal: 1,
                        entityId: Globals.DefaultEntityId,
                        // My Defaults
                        market: "CA",
                        includeExternal: "audio"
                    )
                ).Value.Deserialize<SpotifyplusSearchArtistsResponse>(_jsonOptions);

                string artistId = artistResult?.Result?.Items?[0]?.Id ??
                    throw new TargetException($"The artist {e?.artist} could not be found.");

                SpotifyPlusGetArtistAlbumsResponse? result = (
                    await services.Spotifyplus.GetArtistAlbumsAsync(
                        artistId: artistId,
                        entityId: Globals.DefaultEntityId,
                        market: "CA"
                    )
                ).Value.Deserialize<SpotifyPlusGetArtistAlbumsResponse>(_jsonOptions);

                List<ArtistAlbumItem> albums = result?.Result?.Items ??
                    throw new TargetException($"No albums found for artist {e.artist}");

                ExtractedResult<ArtistAlbumItem> match = Process.ExtractOne(
                    new ArtistAlbumItem { Name = e.album?.ToLower(_cultureInfo) },
                    albums,
                    new Func<ArtistAlbumItem, string>((item) =>
                        (item.Name ?? "").ToLower(_cultureInfo))
                );

                uri = match.Value?.Uri ??
                    throw new TargetException($"No matches found for album {e.album}");
            }
            else
            {
                SpotifyplusSearchAlbumsResponse? result = (
                    await services.Spotifyplus.SearchAlbumsAsync(
                        criteria: $"{e?.album}",
                        limitTotal: 1,
                        entityId: Globals.DefaultEntityId,
                        // My Defaults
                        market: "CA",
                        includeExternal: "audio"
                    )
                ).Value.Deserialize<SpotifyplusSearchAlbumsResponse>(_jsonOptions);

                uri = result?.Result?.Items?[0]?.Uri ??
                    throw new TargetException(
                        $"The album {e?.album}{(e?.artist is null ? "" : $" by {e?.artist}")} could not be found."
                    );
            }

            services.Spotifyplus.PlayerMediaPlayContext(
                contextUri: uri,
                entityId: Globals.DefaultEntityId,
                deviceId: Globals.DefaultDevId,
                // My Defaults
                positionMs: 0,
                delay: 0.50
            );
        }

        public PlayAlbum(IHaContext ha, Services services)
        {
            ha.RegisterServiceCallBack<PlayAlbumData>(
                "spotify_play_album",
                (e) => Globals.RunAsyncSpotifyCallback(services, e, CallBack)
            );
        }
    }
}