using JSMR.Application.Enums;
using JSMR.Application.Scanning;
using JSMR.Application.Scanning.Ports;
using JSMR.Domain.Enums;
using JSMR.Infrastructure.Common.Time;
using JSMR.Worker.Options;
using JSMR.Worker.UI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog.Core;
using Spectre.Console;
using System.Globalization;

namespace JSMR.Worker.Services;

public sealed class PagedScanRunner(
    ILogger<PagedScanRunner> log,
    IServiceProvider serviceProvider,
    ICheckpointStore checkpoints)
{
    public async Task RunAsync(ScanOptions options, CancellationToken cancellationToken)
    {
        if (Enum.TryParse(options.Locale, ignoreCase: true, out Locale locale) == false)
            throw new ArgumentException($"Unknown locale '{options.Locale}'.");

        int pageSize = options.PageSize ?? 100;

        int startPage = options.StartPage
            ?? (await checkpoints.GetLastPageAsync(options.Locale, cancellationToken)).GetValueOrDefault(0) + 1;

        ITimeProvider timeProvider = serviceProvider.GetRequiredService<ITimeProvider>();

        log.LogInformation("Scanning on {ScanTime}...", timeProvider.Now().DateTime.ToString(CultureInfo.CurrentCulture));

        while (!cancellationToken.IsCancellationRequested)
        {
            int currentPage = startPage;
            int? end = options.EndPage;

            // Iterate until empty page or end reached
            for (; !cancellationToken.IsCancellationRequested && (!end.HasValue || currentPage <= end.Value); currentPage++)
            {
                using var scope = serviceProvider.CreateScope();
                ScanVoiceWorksHandler handler = scope.ServiceProvider.GetRequiredService<ScanVoiceWorksHandler>();

                //log.LogInformation("Scanning page {Page} (size {Size}, locale {Locale})…", currentPage, pageSize, locale);
                CliUi.PageHeader(currentPage, end);

                ScanVoiceWorksRequest request = new(
                    PageNumber: currentPage,
                    PageSize: 100,
                    Locale: locale
                );

                ScanVoiceWorksResponse response = await handler.HandleAsync(request, cancellationToken);

                if (response.EndOfResults)
                {
                    CliUi.Information($"Reached end of results at page {currentPage}. Stopping scan.");
                    break;
                }

                int newUpcoming = response.Results.Count(x => x.UpdateStatus == VoiceWorkStatus.NewAndUpcoming);

                //if (newUpcoming > 0)
                //    AnsiConsole.MarkupLine($"    - {newUpcoming} new upcoming work(s)");

                int newRelease = response.Results.Count(x => x.UpdateStatus == VoiceWorkStatus.NewRelease);

                //if (newRelease > 0)
                //    updatedInfo.Add($"{newRelease} new work(s) on sale");

                CliUi.PageHighlights(newUpcoming, newRelease);

                IEnumerable<VoiceWorkUpsertResult> resultsWithIssues = response.Results.Where(x => x.Issues.Count > 0);

                // TODO: Later
                foreach (VoiceWorkUpsertResult resultWithIssues in resultsWithIssues)
                {
                    //log.LogWarning($"PRoblem with {resultWithIssues.}")
                    //string messageToDisplay = $"{scannedVoiceWork.ProductId} - {scannedVoiceWork.ProductName} -- {message}";

                    //Console.WriteLine(messageToDisplay);
                    //messages.Add(messageToDisplay);

                    string productId = resultWithIssues.ProductId;
                    string title = resultWithIssues.Title;
                    string[] messages = [.. resultWithIssues.Issues.Select(x => x.Message)];

                    CliUi.PageErrors($"{productId} - {title}", messages);
                }

                // Save checkpoint
                await checkpoints.SaveLastPageAsync(options.Locale, currentPage, cancellationToken);
            }

            if (!options.Watch) break;

            log.LogInformation("Watch mode: sleeping {Interval}…", options.Interval);
            await Task.Delay(options.Interval, cancellationToken);

            // Compute next “start” for next cycle:
            // - If you want to re-scan the latest N pages every loop to catch late updates,
            //   modify logic here (e.g., start = Math.Max(1, current - 2))
            startPage = currentPage; // continue from where we left off
        }
    }
}