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

namespace Microsoft.AspNetCore.Rewrite.ApacheModRewrite;

internal static class FlagParser
{
    private static readonly IDictionary<string, FlagType> _ruleFlagLookup = new Dictionary<string, FlagType>(StringComparer.OrdinalIgnoreCase) {
            { "b", FlagType.EscapeBackreference},
            { "c", FlagType.Chain },
            { "chain", FlagType.Chain},
            { "co", FlagType.Cookie },
            { "cookie", FlagType.Cookie },
            { "dpi", FlagType.DiscardPath },
            { "discardpath", FlagType.DiscardPath },
            { "e", FlagType.Env},
            { "env", FlagType.Env},
            { "end", FlagType.End },
            { "f", FlagType.Forbidden },
            { "forbidden", FlagType.Forbidden },
            { "g", FlagType.Gone },
            { "gone", FlagType.Gone },
            { "h", FlagType.Handler },
            { "handler", FlagType.Handler },
            { "l", FlagType.Last },
            { "last", FlagType.Last },
            { "n", FlagType.Next },
            { "next", FlagType.Next },
            { "nc", FlagType.NoCase },
            { "nocase", FlagType.NoCase },
            { "ne", FlagType.NoEscape },
            { "noescape", FlagType.NoEscape },
            { "ns", FlagType.NoSubReq },
            { "nosubreq", FlagType.NoSubReq },
            { "or", FlagType.Or },
            { "ornext", FlagType.Or },
            { "p", FlagType.Proxy },
            { "proxy", FlagType.Proxy },
            { "pt", FlagType.PassThrough },
            { "passthrough", FlagType.PassThrough },
            { "qsa", FlagType.QSAppend },
            { "qsappend", FlagType.QSAppend },
            { "qsd", FlagType.QSDiscard },
            { "qsdiscard", FlagType.QSDiscard },
            { "qsl", FlagType.QSLast },
            { "qslast", FlagType.QSLast },
            { "r", FlagType.Redirect },
            { "redirect", FlagType.Redirect },
            { "s", FlagType.Skip },
            { "skip", FlagType.Skip },
            { "t", FlagType.Type },
            { "type", FlagType.Type },
        };

    public static Flags Parse(string flagString)
    {
        if (string.IsNullOrEmpty(flagString))
        {
            throw new ArgumentException("Argument cannot be null or empty string.", nameof(flagString));
        }

        // Check that flags are contained within []
        // Guaranteed to have a length of at least 1 here, so this will never throw for indexing.
        if (!(flagString[0] == '[' && flagString[flagString.Length - 1] == ']'))
        {
            throw new FormatException("Flags should start and end with square brackets: [flags]");
        }

        // Lexing esque step to split all flags.
        // Invalid syntax to have any spaces.
        var tokens = flagString.Substring(1, flagString.Length - 2).Split(',');
        var flags = new Flags();
        Span<Range> hasPayload = stackalloc Range[3];
        foreach (var token in tokens)
        {
            var tokenSpan = token.AsSpan();
            var length = tokenSpan.Split(hasPayload, '=');

            FlagType flag;
            if (!_ruleFlagLookup.TryGetValue(tokenSpan[hasPayload[0]].ToString(), out flag))
            {
                throw new FormatException($"Unrecognized flag: '{tokenSpan[hasPayload[0]]}'");
            }

            if (length == 2)
            {
                flags.SetFlag(flag, tokenSpan[hasPayload[1]].ToString());
            }
            else
            {
                flags.SetFlag(flag, string.Empty);
            }
        }
        return flags;
    }
}
