using Npgsql;
using Shared.Excel;

namespace Modules.Common.Imports.Jobs;

public sealed class ValidateImportJob(
    ImportRepository repository,
    ImportDefinitionRegistry registry,
    ClosedXmlWorkbookService workbook,
    ImportProgressPublisher progress,
    NpgsqlDataSource dataSource)
{
    public async Task RunAsync(Guid sessionId, CancellationToken ct)
    {
        try
        {
            var (importType, mapping) = await repository.GetWorkDefinitionAsync(sessionId, ct);
            var definition = registry.Get(importType);

            var targetToSource = mapping
                .Where(x => !string.IsNullOrWhiteSpace(x.TargetField))
                .ToDictionary(x => x.TargetField!, x => x.SourceColumn, StringComparer.OrdinalIgnoreCase);

            var (contentType, bytes) = await repository.GetFileAsync(sessionId, ct);
            await using var stream = new MemoryStream(bytes, writable: false);
            var rows = workbook.ReadRows(stream, definition.Definition.MaxRows).ToArray();
            await progress.PublishAsync(new ImportProgressEvent(
                sessionId, ImportStatuses.Validating, 15, rows.Length, 0, 0, 0, 0,
                "참조 데이터를 확인하고 있습니다."), ct);

            await using var connection = await dataSource.OpenConnectionAsync(ct);
            var results = await definition.ValidateAsync(rows, targetToSource, connection, ct);
            var valid = results.Count(x => x.IsValid);
            var invalid = results.Count - valid;
            var warnings = results.Count(x => x.Warnings.Count > 0);

            await progress.PublishAsync(new ImportProgressEvent(
                sessionId, ImportStatuses.Validating, 80, rows.Length, rows.Length,
                valid, invalid, warnings, "검증 결과를 저장하고 있습니다."), ct);

            await repository.ReplaceRowsAsync(sessionId, results, ct);
            await progress.PublishAsync(new ImportProgressEvent(
                sessionId, ImportStatuses.Validated, 100, rows.Length, rows.Length,
                valid, invalid, warnings, "검증이 완료되었습니다."), ct);
        }
        catch (Exception)
        {
            await repository.SetStatusAsync(sessionId, ImportStatuses.Failed, 100, ct);
            throw;
        }
    }
}
