// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using Xunit;

namespace Microsoft.AspNetCore.Internal;

public class QueryStringEnumerableTest
{
    [Fact]
    public void ParseQueryWithUniqueKeysWorks()
    {
        Assert.Collection(Parse("?key1=value1&key2=value2"),
            kvp => AssertKeyValuePair("key1", "value1", kvp),
            kvp => AssertKeyValuePair("key2", "value2", kvp));
    }

    [Fact]
    public void ParseQueryWithoutQuestionmarkWorks()
    {
        Assert.Collection(Parse("key1=value1&key2=value2"),
            kvp => AssertKeyValuePair("key1", "value1", kvp),
            kvp => AssertKeyValuePair("key2", "value2", kvp));
    }

    [Fact]
    public void ParseQueryWithDuplicateKeysGroups()
    {
        Assert.Collection(Parse("?key1=valueA&key2=valueB&key1=valueC"),
            kvp => AssertKeyValuePair("key1", "valueA", kvp),
            kvp => AssertKeyValuePair("key2", "valueB", kvp),
            kvp => AssertKeyValuePair("key1", "valueC", kvp));
    }

    [Fact]
    public void ParseQueryWithEmptyValuesWorks()
    {
        Assert.Collection(Parse("?key1=&key2="),
            kvp => AssertKeyValuePair("key1", string.Empty, kvp),
            kvp => AssertKeyValuePair("key2", string.Empty, kvp));
    }

    [Fact]
    public void ParseQueryWithEmptyKeyWorks()
    {
        Assert.Collection(Parse("?=value1&="),
            kvp => AssertKeyValuePair(string.Empty, "value1", kvp),
            kvp => AssertKeyValuePair(string.Empty, string.Empty, kvp));
    }

    [Fact]
    public void ParseQueryWithEncodedKeyWorks()
    {
        Assert.Collection(Parse("?fields+%5BtodoItems%5D"),
            kvp => AssertKeyValuePair("fields+%5BtodoItems%5D", string.Empty, kvp));
    }

    [Fact]
    public void ParseQueryWithEncodedValueWorks()
    {
        Assert.Collection(Parse("?=fields+%5BtodoItems%5D"),
            kvp => AssertKeyValuePair(string.Empty, "fields+%5BtodoItems%5D", kvp));
    }

    [Theory]
    [InlineData("?")]
    [InlineData("")]
    [InlineData(null)]
    [InlineData("?&&")]
    public void ParseEmptyOrNullQueryWorks(string queryString)
    {
        Assert.Empty(Parse(queryString));
    }

    [Fact]
    public void ParseIgnoresEmptySegments()
    {
        Assert.Collection(Parse("?&key1=value1&&key2=value2&"),
            kvp => AssertKeyValuePair("key1", "value1", kvp),
            kvp => AssertKeyValuePair("key2", "value2", kvp));
    }

    [Theory]
    [InlineData("?a+b=c+d", "a b", "c d")]
    [InlineData("? %5Bkey%5D = %26value%3D ", " [key] ", " &value= ")]
    [InlineData("?+", " ", "")]
    [InlineData("?=+", "", " ")]
    public void DecodingWorks(string queryString, string expectedDecodedName, string expectedDecodedValue)
    {
        foreach (var kvp in new QueryStringEnumerable(queryString))
        {
            Assert.Equal(expectedDecodedName, kvp.DecodeName().ToString());
            Assert.Equal(expectedDecodedValue, kvp.DecodeValue().ToString());
        }
    }

    [Fact]
    public void DecodingReusesMemoryIfDecodingNotNeeded()
    {
        foreach (var kvp in new QueryStringEnumerable("?key=value"))
        {
            Assert.True(kvp.EncodedName.Equals(kvp.DecodeName()));
            Assert.True(kvp.EncodedValue.Equals(kvp.DecodeValue()));
        }
    }

    private static void AssertKeyValuePair(string expectedKey, string expectedValue, (string key, string value) actual)
    {
        Assert.Equal(expectedKey, actual.key);
        Assert.Equal(expectedValue, actual.value);
    }

    private static IReadOnlyList<(string key, string value)> Parse(string query)
    {
        var result = new List<(string key, string value)>();
        var enumerable = new QueryStringEnumerable(query);
        foreach (var pair in enumerable)
        {
            result.Add((pair.EncodedName.ToString(), pair.EncodedValue.ToString()));
        }

        return result;
    }
}
