using JSMR.Application.Common.Caching;
using JSMR.Application.Integrations.DLSite.Models;
using JSMR.Application.Integrations.Ports;
using JSMR.Application.Scanning.Contracts;
using JSMR.Application.Scanning.Ports;
using Microsoft.Extensions.DependencyInjection;

namespace JSMR.Application.Scanning;

public sealed class ScanVoiceWorksHandler(
    IServiceProvider serviceProvider,
    IDLSiteClient dlsiteClient,
    ISpamCircleCache spamCircleCache,
    IVoiceWorkSearchUpdater searchUpdater)
{
    public async Task<ScanVoiceWorksResponse> HandleAsync(ScanVoiceWorksRequest request, CancellationToken cancellationToken)
    {
        IVoiceWorksScanner? scanner = serviceProvider.GetKeyedService<IVoiceWorksScanner>(request.Locale);
        IVoiceWorkUpdater? updater = serviceProvider.GetKeyedService<IVoiceWorkUpdater>(request.Locale);

        if (scanner is null || updater is null)
            return new();

        VoiceWorkScanOptions options = new(
            PageNumber: request.PageNumber,
            PageSize: request.PageSize,
            ExcludedMakerIds: await spamCircleCache.GetAsync(cancellationToken),
            ExcludePartiallyAIGeneratedWorks: true,
            ExcludeAIGeneratedWorks: true
        );

        IReadOnlyList<DLSiteWork> works = await scanner.ScanPageAsync(options, cancellationToken);

        if (works.Count == 0)
            return new();

        string[] productIds = [.. works.Where(x => !string.IsNullOrWhiteSpace(x.ProductId)).Select(x => x.ProductId!)];
        VoiceWorkDetailCollection voiceWorkDetails = await dlsiteClient.GetVoiceWorkDetailsAsync(productIds, cancellationToken);

        VoiceWorkIngest[] ingests = [.. works.Select(work =>
        {
            voiceWorkDetails.TryGetValue(work.ProductId!, out VoiceWorkDetails? value);
            return VoiceWorkIngest.From(work, value);
        })];

        VoiceWorkUpsertResult[] upsertResults = await updater.UpsertAsync(ingests, cancellationToken);
        int[] voiceWorkIds = [.. upsertResults.Where(x => x.VoiceWorkId.HasValue).Select(x => x.VoiceWorkId!.Value)];

        await searchUpdater.UpdateAsync(voiceWorkIds, cancellationToken);

        return new();
    }
}