using System.Text.RegularExpressions;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Shared.Contracts.Generated;
using Shared.Problems;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace Shared.Contracts;

public static class KbxApiContractSwaggerExtensions
{
    public static void AddKbxApiContracts(this SwaggerGenOptions options)
    {
        options.OperationFilter<KbxApiContractOperationFilter>();
        options.OperationFilter<KbxProblemOpenApiOperationFilter>();
    }
}

public sealed class KbxApiContractOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var method = context.ApiDescription.HttpMethod?.ToUpperInvariant();
        var relative = "/" + (context.ApiDescription.RelativePath ?? string.Empty).TrimStart('/');
        var normalized = Normalize(relative);
        var contract = KbxApiCatalog.All.FirstOrDefault(x =>
            x.Method == method && Normalize(x.Path) == normalized);
        if (contract is null) return;

        operation.OperationId = contract.Id;
        operation.Extensions["x-kbx-permission"] = new OpenApiString(contract.Permission ?? string.Empty);
        operation.Extensions["x-kbx-idempotency"] = new OpenApiString(contract.Idempotency);
        operation.Extensions["x-kbx-kind"] = new OpenApiString(contract.Kind);
    }

    private static string Normalize(string value) =>
        Regex.Replace(value, @"\{([^}:]+):[^}]+\}", "{$1}").ToLowerInvariant();
}

public sealed class KbxProblemOpenApiOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var problem = new OpenApiSchema
        {
            OneOf = new List<OpenApiSchema>
            {
                context.SchemaGenerator.GenerateSchema(typeof(KbxValidationProblem), context.SchemaRepository),
                context.SchemaGenerator.GenerateSchema(typeof(KbxBusinessProblem), context.SchemaRepository),
                context.SchemaGenerator.GenerateSchema(typeof(KbxConflictProblem), context.SchemaRepository),
                context.SchemaGenerator.GenerateSchema(typeof(KbxPermissionProblem), context.SchemaRepository),
                context.SchemaGenerator.GenerateSchema(typeof(KbxNotFoundProblem), context.SchemaRepository),
                context.SchemaGenerator.GenerateSchema(typeof(KbxIntegrationProblem), context.SchemaRepository),
                context.SchemaGenerator.GenerateSchema(typeof(KbxSystemProblem), context.SchemaRepository),
            }
        };

        Add(operation, "400", "Validation problem", problem);
        Add(operation, "403", "Permission problem", problem);
        Add(operation, "404", "Not found problem", problem);
        Add(operation, "409", "Conflict or business rule problem", problem);
        Add(operation, "422", "Business rule problem", problem);
        Add(operation, "500", "System problem", problem);
    }

    private static void Add(OpenApiOperation operation, string status, string description, OpenApiSchema schema)
    {
        if (operation.Responses.ContainsKey(status)) return;
        operation.Responses[status] = new OpenApiResponse
        {
            Description = description,
            Content = new Dictionary<string, OpenApiMediaType>
            {
                ["application/json"] = new() { Schema = schema }
            }
        };
    }
}
