using FNZCM.BlazorWasm.Models;
using FNZCM.BlazorWasm.Models.Fe;
using FNZCM.BlazorWasm.UI.Components;
using FNZCM.BlazorWasm.Utility;
using FNZCM.Shared.Helpers;
using FNZCM.Shared.MediaModels;

namespace FNZCM.BlazorWasm.Helpers
{
    public class FnzDataSetHelper
    {
        private readonly ApiClient api;
        private readonly FnzDataSetSingleto dataSet;

        public FnzDataSetHelper(ApiClient api, FnzDataSetSingleto dataset)
        {
            this.api = api;
            this.dataSet = dataset;
        }

        public async Task InitFeModulesAsync(ProgressBar[] progress)
        {
            var mediaTagsT = api.GetMediaTagsAsync(progress);
            var librariesT = api.GetLibrariesAsync(progress);

            await Task.WhenAll(librariesT, mediaTagsT);

            var libraries = librariesT.Result;
            var mediaTags = mediaTagsT.Result;

            var apiBase = api.BaseAddress.ToString();

            var lstAllTrack = new List<FeTrack>();
            var lstAllTrackSet = new List<FeTrackSet>();
            var lstAllDisc = new List<FeDisc>();
            var lstAllLibrry = new List<FeLibrary>();

            var currentDisc = 0;
            var numberOfDisc = libraries.Sum(p => p.Value.Discs.Count);

            foreach (var lib in libraries.OrderBy(p => p.Key))
            {
                var feLib = new FeLibrary
                {
                    Key = lib.Key,
                    Name = lib.Value.Name,
                };
                lstAllLibrry.Add(feLib);

                var lstDiscOfLib = new List<FeDisc>();
                foreach (var disc in lib.Value.Discs.OrderByDescending(p => p.Key))
                {
                    await progress[2].SetProgress(((float)(currentDisc++)) / numberOfDisc, $"Parsing model {currentDisc}/{numberOfDisc}...");

                    var feDisc = new FeDisc
                    {
                        LibKey = lib.Key,
                        Lib = feLib,

                        Key = disc.Key,
                        Name = disc.Value.Name,

                        CoverPath = $"{apiBase}cover/{lib.Key.UrlEscape()}/{disc.Key.UrlEscape()}/cover.jpg",
                    };
                    lstAllDisc.Add(feDisc);
                    lstDiscOfLib.Add(feDisc);

                    // BK
                    if (disc.Value.Bks?.Any() == true)
                    {
                        var feBkOfDisc = new List<FeBk>();
                        foreach (var item in disc.Value.Bks.OrderBy(p => p.Key))
                        {
                            feBkOfDisc.Add(new FeBk
                            {
                                Key = item.Key,
                                Name = item.Value,
                                Path = $"{apiBase}bk/{lib.Key.UrlEscape()}/{disc.Key.UrlEscape()}/{item.Key.UrlEscape()}",
                            });
                        }
                        feDisc.Bks = feBkOfDisc.ToArray();
                    }

                    // Trask Set
                    var lstTrackSetOfDisc = new List<FeTrackSet>();

                    // Trask Set Main
                    {
                        var feTsMain = new FeTrackSet
                        {
                            LibKey = lib.Key,
                            Lib = feLib,
                            DiscKey = disc.Key,
                            Disc = feDisc,
                            Key = "main",
                            Name = "Main",
                            M3U8Path = $"{apiBase}list/{lib.Key.UrlEscape()}/{disc.Key.UrlEscape()}/playlist.m3u8",
                        };
                        lstAllTrackSet.Add(feTsMain);
                        lstTrackSetOfDisc.Add(feTsMain);

                        var lstTracksOfTsMain = new List<FeTrack>();
                        foreach (var t in disc.Value.MainTracks.OrderBy(p => p.Key))
                        {
                            MediaTag mediaTag = mediaTags.GetEx($"/media/{lib.Key}/{disc.Key}/{t.Key}");
                            var fet = new FeTrack
                            {
                                LibKey = lib.Key,
                                Lib = feLib,
                                DiscKey = disc.Key,
                                Disc = feDisc,
                                TrackSetKey = "main",
                                TrackSet = feTsMain,
                                Key = t.Key,
                                Name = t.Value,
                                Path = $"{apiBase}media/{lib.Key.UrlEscape()}/{disc.Key.UrlEscape()}/{t.Key.UrlEscape()}",
                                Tag = MappingTag(mediaTag)
                            };
                            lstAllTrack.Add(fet);

                            if (lstAllTrack.Count % 250 == 0) await Task.Delay(1);

                            lstTracksOfTsMain.Add(fet);
                        }
                        feTsMain.Tracks = lstTracksOfTsMain.ToArray();
                        feTsMain.TotalBytes = feTsMain.Tracks.Sum(p => p.Tag?.Length ?? 0);
                        feTsMain.TotalDuration = feTsMain.Tracks.Sum(p => p.Tag?.Duration ?? 0);
                    }

                    // Trask Set Sub
                    {
                        foreach (var tSubSet in disc.Value.SubTracks.OrderBy(p => p.Key))
                        {
                            var feTsSub = new FeTrackSet
                            {
                                LibKey = lib.Key,
                                Lib = feLib,
                                DiscKey = disc.Key,
                                Disc = feDisc,
                                Key = tSubSet.Key,
                                Name = tSubSet.Value.Name,
                                M3U8Path = $"{apiBase}list/{lib.Key.UrlEscape()}/{disc.Key.UrlEscape()}/{tSubSet.Key.UrlEscape()}/playlist.m3u8",
                            };
                            lstAllTrackSet.Add(feTsSub);

                            if (lstAllTrack.Count % 250 == 0) await Task.Delay(1);

                            lstTrackSetOfDisc.Add(feTsSub);

                            var lstTracksOfTsSub = new List<FeTrack>();
                            foreach (var t in tSubSet.Value.Tracks.OrderBy(p => p.Key))
                            {
                                MediaTag mediaTag = mediaTags.GetEx($"/media/{lib.Key}/{disc.Key}/{tSubSet.Key}/{t.Key}");
                                var fet = new FeTrack
                                {
                                    LibKey = lib.Key,
                                    Lib = feLib,
                                    DiscKey = disc.Key,
                                    Disc = feDisc,
                                    TrackSetKey = tSubSet.Key,
                                    TrackSet = feTsSub,
                                    Key = t.Key,
                                    Name = t.Value,
                                    Path = $"{apiBase}media/{lib.Key.UrlEscape()}/{disc.Key.UrlEscape()}/{tSubSet.Key.UrlEscape()}/{t.Key.UrlEscape()}",
                                    Tag = MappingTag(mediaTag)
                                };
                                lstAllTrack.Add(fet);
                                lstTracksOfTsSub.Add(fet);
                            }
                            feTsSub.Tracks = lstTracksOfTsSub.ToArray();
                            feTsSub.TotalBytes = feTsSub.Tracks.Sum(p => p.Tag?.Length ?? 0);
                            feTsSub.TotalDuration = feTsSub.Tracks.Sum(p => p.Tag?.Duration ?? 0);
                        }// end foreach sub track set
                    }
                    feDisc.TrackSets = lstTrackSetOfDisc.ToArray();
                }// end foreach disc

                feLib.Discs = lstDiscOfLib.ToArray();

                var lstCatalogOfLib = new List<FeLibraryCatalog>();
                var trackSetKeys = feLib.Discs.SelectMany(p => p.TrackSets.Select(q => q.Key)).Distinct().ToArray();
                foreach (var tsKey in trackSetKeys)
                {
                    var cat = new FeLibraryCatalog
                    {
                        Name = tsKey.ToUpper(),
                        PlaylistPath = tsKey == "main"
                            ? $"{apiBase}lib_list/{lib.Key.UrlEscape()}/playlist.m3u8"
                            : $"{apiBase}lib_list/{lib.Key.UrlEscape()}/{tsKey.UrlEscape()}/playlist.m3u8",
                        TotalBytes = feLib.Discs
                            .Sum(p => p.TrackSets
                                .Where(p => p.Key == tsKey)
                                .Sum(q => q.Tracks
                                    .Sum(r => r.Tag?.Length ?? 0)
                                )
                            ),
                        TotalDuration = feLib.Discs
                            .Sum(p => p.TrackSets
                                .Where(p => p.Key == tsKey)
                                .Sum(q => q.Tracks
                                    .Sum(r => r.Tag?.Duration ?? 0)
                                )
                            ),
                    };
                    lstCatalogOfLib.Add(cat);
                }
                feLib.Catalogs = lstCatalogOfLib.ToArray();
            }// end foreach lib

            await progress[2].SetProgress(((float)(currentDisc)) / numberOfDisc, $"Parsing model {currentDisc}/{numberOfDisc}...OK", true);

            dataSet.AllTracks = lstAllTrack.ToArray();
            dataSet.AllTracksSet = lstAllTrackSet.ToArray();
            dataSet.AllDisc = lstAllDisc.ToArray();
            dataSet.AllLibrary = lstAllLibrry.ToArray();
        }

        private static FeMediaTag MappingTag(MediaTag mediaTag)
        {
            if (mediaTag == null) return null;

            var m = new FeMediaTag
            {
                Album = mediaTag.Album,
                DiscNum = mediaTag.DiscNum,
                TrackNum = mediaTag.DiscNum,
                Title = mediaTag.Title,
                Artist = mediaTag.Artist,
                Duration = mediaTag.Duration,
                Length = mediaTag.Length,
                Bit = mediaTag.Bit,
                Freq = mediaTag.Freq,
                BitRate = mediaTag.BitRate,
            };

            return m;
        }
    }
}
