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

using System.Diagnostics;
using System.Globalization;
using Microsoft.AspNetCore.Rewrite.UrlActions;

namespace Microsoft.AspNetCore.Rewrite.ApacheModRewrite;

internal sealed class CookieActionFactory
{
    /// <summary>
    ///  Creates a <see cref="ChangeCookieAction" /> <see href="https://httpd.apache.org/docs/current/rewrite/flags.html#flag_co" /> for details.
    /// </summary>
    /// <param name="flagValue">The flag</param>
    /// <returns>The action</returns>
    public static ChangeCookieAction Create(string flagValue)
    {
        ArgumentException.ThrowIfNullOrEmpty(flagValue);

        var i = 0;
        var separator = ':';
        if (flagValue[0] == ';')
        {
            separator = ';';
            i++;
        }

        ChangeCookieAction? action = null;
        var currentField = Fields.Name;
        var start = i;
        for (; i < flagValue.Length; i++)
        {
            if (flagValue[i] == separator)
            {
                var length = i - start;
                SetActionOption(flagValue.Substring(start, length).Trim(), currentField, ref action);

                currentField++;
                start = i + 1;
            }
        }

        if (i != start)
        {
            SetActionOption(flagValue.Substring(start).Trim(new[] { ' ', separator }), currentField, ref action);
        }

        if (currentField < Fields.Domain)
        {
            throw new FormatException(Resources.FormatError_InvalidChangeCookieFlag(flagValue));
        }

        return action!;
    }

    private static void SetActionOption(string value, Fields tokenType, ref ChangeCookieAction? action)
    {
        Debug.Assert(action != null || tokenType == Fields.Name);

        switch (tokenType)
        {
            case Fields.Name:
                action = new ChangeCookieAction(value);
                break;
            case Fields.Value:
                action!.Value = value;
                break;
            case Fields.Domain:
                // despite what spec says, an empty domain field is allowed in mod_rewrite
                // by specifying NAME:VALUE:;
                action!.Domain = string.IsNullOrEmpty(value) || value == ";"
                    ? null
                    : value;
                break;
            case Fields.Lifetime:
                if (string.IsNullOrEmpty(value))
                {
                    break;
                }

                uint minutes;
                if (!uint.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out minutes))
                {
                    throw new FormatException(Resources.FormatError_CouldNotParseInteger(value));
                }

                action!.Lifetime = TimeSpan.FromMinutes(minutes);
                break;
            case Fields.Path:
                action!.Path = value;
                break;
            case Fields.Secure:
                action!.Secure = "secure".Equals(value, StringComparison.OrdinalIgnoreCase)
                    || "true".Equals(value, StringComparison.OrdinalIgnoreCase)
                    || value == "1";
                break;
            case Fields.HttpOnly:
                action!.HttpOnly = "httponly".Equals(value, StringComparison.OrdinalIgnoreCase)
                    || "true".Equals(value, StringComparison.OrdinalIgnoreCase)
                    || value == "1";
                break;
        }
    }

    // order matters
    // see https://httpd.apache.org/docs/current/rewrite/flags.html#flag_co
    private enum Fields
    {
        Name,
        Value,
        Domain,
        Lifetime,
        Path,
        Secure,
        HttpOnly
    }
}
