using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MangaReader.Core.Metadata;
using MangaReader.Core.Pipeline;
using MangaReader.Core.Search;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Media.Imaging;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;

namespace MangaReader.WinUI.ViewModels;

public partial class SearchViewModel(
    IMangaSearchCoordinator searchCoordinator,
    IMangaMetadataCoordinator metadataCoordinator,
    IMangaPipeline pipeline) : ViewModelBase
{
    private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();

    private CancellationTokenSource? _cancellationTokenSource;

    private string? _keyword;

    public string? Keyword
    {
        get
        {
            return _keyword;
        }
        set
        {
            SetProperty(ref _keyword, value);
        }
    }

    private ObservableCollection<MangaSearchResult> _searchResults = [];

    public ObservableCollection<MangaSearchResult> SearchResults
    {
        get
        {
            return _searchResults;
        }
        set
        {
            SetProperty(ref _searchResults, value);
        }
    }

    private ObservableCollection<ObservableMangaSearchResult> _searchResults2 = [];

    public ObservableCollection<ObservableMangaSearchResult> SearchResults2
    {
        get
        {
            return _searchResults2;
        }
        set
        {
            SetProperty(ref _searchResults2, value);
        }
    }

    public ICommand SearchCommand => new AsyncRelayCommand(SearchAsync);
    //public ICommand ImportCommand => new AsyncRelayCommand(ImportAsync);

    public async Task SearchAsync()
    {
        if (string.IsNullOrWhiteSpace(Keyword))
            return;

        _cancellationTokenSource?.Cancel();
        _cancellationTokenSource = new();

        Dictionary<string, MangaSearchResult[]> result = await searchCoordinator.SearchAsync(Keyword, _cancellationTokenSource.Token);

        List<MangaSearchResult> searchResults = [];
        List<ObservableMangaSearchResult> mangaSearchResults = [];

        foreach (var item in result)
        {
            foreach (MangaSearchResult searchResult in item.Value)
            {
                //searchResults.Add(searchResult);

                ObservableMangaSearchResult mangaSearchResult = new()
                {
                    Source = searchResult.Source,
                    Url = searchResult.Url,
                    Title = searchResult.Title,
                    Thumbnail = searchResult.Thumbnail,
                    Description = searchResult.Description,
                    Genres = searchResult.Genres
                };

                Task.Run(() => mangaSearchResult.LoadThumbnailAsync(_dispatcherQueue)); // or defer this if you want lazy loading

                searchResults.Add(searchResult);
                mangaSearchResults.Add(mangaSearchResult);
            }
        }

        SearchResults = new(searchResults);
        SearchResults2 = new(mangaSearchResults);
    }

    public static async Task<BitmapImage?> LoadWebpAsBitmapImageAsync(string? url)
    {
        if (string.IsNullOrWhiteSpace(url))
            return null;

        using var httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0");
        using var webpStream = await httpClient.GetStreamAsync(url);

        using var image = await Image.LoadAsync(webpStream); // from SixLabors.ImageSharp
        using var ms = new MemoryStream();
        //await image.SaveAsPngAsync(ms); // Convert to PNG in memory
        await image.SaveAsJpegAsync(ms);
        ms.Position = 0;

        var bitmap = new BitmapImage();
        await bitmap.SetSourceAsync(ms.AsRandomAccessStream());

        return bitmap;
    }

    public async Task ImportAsync(ObservableMangaSearchResult searchResult, CancellationToken cancellationToken)
    {
        IMangaMetadataProvider metadataProvider = metadataCoordinator.GetProvider(searchResult.Source);
        SourceManga? sourceManga = await metadataProvider.GetMangaAsync(searchResult.Url, cancellationToken);

        if (sourceManga == null)
            return;

        MangaMetadataPipelineRequest request = new()
        {
            SourceName = searchResult.Source,
            SourceUrl = searchResult.Url,
            SourceManga = sourceManga,
        };

        await pipeline.RunMetadataAsync(request, cancellationToken);
    }
}

public partial class ObservableMangaSearchResult : ObservableObject
{
    public required string Source { get; init; }
    public required string Url { get; init; }
    public string? Title { get; init; }
    public string? Description { get; init; }
    public string? Thumbnail { get; init; }
    public string[] Genres { get; init; } = [];

    private BitmapImage? _thumbnailBitmap;

    public BitmapImage? ThumbnailBitmap
    {
        get
        {
            return _thumbnailBitmap;
        }
        set
        {
            SetProperty(ref _thumbnailBitmap, value);
        }
    }

    public async Task LoadThumbnailAsync(DispatcherQueue dispatchQueue)
    {
        if (string.IsNullOrWhiteSpace(Thumbnail))
            return;

        try
        {
            using var httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0");

            using var stream = await httpClient.GetStreamAsync(Thumbnail);
            using var image = await Image.LoadAsync(stream); // Important: use a pixel type

            using var ms = new MemoryStream();
            await image.SaveAsJpegAsync(ms); // or SaveAsPngAsync
            ms.Position = 0;

            TaskCompletionSource taskCompletionSource = new();

            dispatchQueue.TryEnqueue(async () => {
                var bitmap = new BitmapImage();
                await bitmap.SetSourceAsync(ms.AsRandomAccessStream());

                ThumbnailBitmap = bitmap;

                taskCompletionSource.SetResult();
            });

            taskCompletionSource.Task.GetAwaiter().GetResult();

            //var bitmap = new BitmapImage();
            //await bitmap.SetSourceAsync(ms.AsRandomAccessStream());

            //ThumbnailBitmap = bitmap;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"[Thumbnail Load Failed] {ex.Message}");
        }
    }
}