using System.Net.Http.Headers;
using System.Text.Json;
using System.Text.Json.Serialization;
using ApiOPE.Contracts;

namespace ApiOPE.Services;

public sealed record GenericOperationRequestResult(
    OpeDataEnvelope? Request,
    IReadOnlyDictionary<string, string> Errors)
{
    public bool IsValid => Request is not null && Errors.Count == 0;
}

public static class GenericOperationRequestReader
{
    private const string ExpectedField = "FIELD_0";

    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
    {
        PropertyNameCaseInsensitive = false,
        UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
        MaxDepth = 16
    };

    public static async Task<GenericOperationRequestResult> ReadAsync(
        HttpRequest request,
        CancellationToken cancellationToken)
    {
        var errors = new Dictionary<string, string>(StringComparer.Ordinal);

        if (!MediaTypeHeaderValue.TryParse(request.ContentType, out var contentType) ||
            !string.Equals(
                contentType.MediaType,
                OpeMediaTypes.GenericOperationRequest,
                StringComparison.OrdinalIgnoreCase))
        {
            errors[ExpectedField] = OpeFieldErrors.UnexpectedFormat;
            return new GenericOperationRequestResult(null, errors);
        }

        OpeDataEnvelope? envelope;
        try
        {
            envelope = await JsonSerializer.DeserializeAsync<OpeDataEnvelope>(
                request.Body,
                JsonOptions,
                cancellationToken);
        }
        catch (JsonException)
        {
            errors[ExpectedField] = OpeFieldErrors.UnexpectedFormat;
            return new GenericOperationRequestResult(null, errors);
        }

        if (envelope?.Data is null)
        {
            errors[ExpectedField] = OpeFieldErrors.Expected;
            return new GenericOperationRequestResult(null, errors);
        }

        foreach (var field in envelope.Data)
        {
            if (!string.Equals(field.Key, ExpectedField, StringComparison.Ordinal))
            {
                errors[field.Key] = OpeFieldErrors.NotExpected;
                continue;
            }

            if (field.Value is null ||
                !string.Equals(field.Value.Type, "STRING", StringComparison.Ordinal) ||
                string.IsNullOrWhiteSpace(field.Value.Value))
            {
                errors[field.Key] = OpeFieldErrors.UnexpectedFormat;
            }
        }

        if (!envelope.Data.ContainsKey(ExpectedField))
        {
            errors[ExpectedField] = OpeFieldErrors.Expected;
        }

        if (envelope.Data.Count > 10)
        {
            foreach (var field in envelope.Data.Keys.Where(key => key != ExpectedField))
            {
                errors[field] = OpeFieldErrors.NotExpected;
            }
        }

        return new GenericOperationRequestResult(envelope, errors);
    }
}
