using FastEndpoints;
using Shared.Excel;

namespace Modules.Common.Imports.Endpoints;

public sealed class CreateSessionRequest
{
    public string ImportType { get; init; } = string.Empty;
}

public sealed class CreateSessionEndpoint(
    ImportDefinitionRegistry registry,
    ImportRepository repository,
    ClosedXmlWorkbookService workbook,
    ImportMappingEngine mappingEngine,
    IImportMappingSuggester mappingSuggester,
    XlsxSafetyInspector safety)
    : Endpoint<CreateSessionRequest, ImportSessionDto>
{
    public override void Configure()
    {
        Post("/api/imports/sessions");
        AllowFileUploads();
        Permissions("imports.execute");
    }

    public override async Task HandleAsync(CreateSessionRequest req, CancellationToken ct)
    {
        if (!registry.TryGet(req.ImportType, out var importDefinition) || importDefinition is null)
        {
            await Send.NotFoundAsync(ct);
            return;
        }

        var file = Files.FirstOrDefault();
        if (file is null || file.Length == 0)
        {
            AddError("Excel 파일을 선택하세요.");
            await Send.ErrorsAsync(cancellation: ct);
            return;
        }

        var definition = importDefinition.Definition;
        if (file.Length > definition.MaxFileSizeBytes)
        {
            AddError($"파일 크기는 최대 {definition.MaxFileSizeBytes / 1024 / 1024}MB입니다.");
            await Send.ErrorsAsync(cancellation: ct);
            return;
        }
        if (!Path.GetExtension(file.FileName).Equals(".xlsx", StringComparison.OrdinalIgnoreCase))
        {
            AddError(".xlsx 파일만 업로드할 수 있습니다.");
            await Send.ErrorsAsync(cancellation: ct);
            return;
        }

        await using var source = file.OpenReadStream();
        using var buffer = new MemoryStream();
        await source.CopyToAsync(buffer, ct);
        var bytes = buffer.ToArray();
        try { safety.EnsureSafe(bytes); }
        catch (InvalidDataException ex)
        {
            AddError(ex.Message);
            await Send.ErrorsAsync(cancellation: ct);
            return;
        }
        await using var read = new MemoryStream(bytes, writable: false);
        var columns = workbook.ReadHeaders(read);
        var tenantId = ImportIdentity.TenantId(User);
        var userId = ImportIdentity.UserId(User);
        var saved = await repository.GetSavedMappingAsync(tenantId, userId, definition.Id, columns, ct);
        var mapping = mappingEngine.Map(columns, definition, saved).ToList();
        var unresolved = mapping.Where(x => x.TargetField is null).Select(x => x.SourceColumn).ToArray();
        if (unresolved.Length > 0)
        {
            var suggestions = await mappingSuggester.SuggestAsync(unresolved, definition, ct);
            var allowed = definition.Fields.Select(x => x.Key).ToHashSet(StringComparer.OrdinalIgnoreCase);
            foreach (var suggestion in suggestions.Where(x => x.TargetField is not null && allowed.Contains(x.TargetField)))
            {
                var index = mapping.FindIndex(x => x.SourceColumn.Equals(suggestion.SourceColumn, StringComparison.OrdinalIgnoreCase) && x.TargetField is null);
                if (index >= 0) mapping[index] = suggestion with { Source = "ai" };
            }
        }
        var sessionId = await repository.CreateSessionAsync(
            tenantId, userId, definition, file.FileName,
            file.ContentType ?? "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            bytes, columns, mapping, ct);

        var session = await repository.GetAsync(sessionId, tenantId, userId, includeErrors: false, ct);
        await Send.OkAsync(session!, ct);
    }
}
