using e_suite.Database.Core.Tables.Sequences;
using e_suite.Modules.SequenceManager.Extensions;
using eSuite.Core.Clock;
using eSuite.Core.Sequences;
using System.Text.RegularExpressions;

namespace e_suite.Modules.SequenceManager;

public static class SequenceNumberBuilder
{
    private const string NumberPattern = @"\[([0-9]+)\]";
    private const string DatePattern = @"\{([MM|YY|YYYY|DD|yy|yyyy|mm|dd]+)\}";

    private static readonly Regex NumberExpression = new(NumberPattern, RegexOptions.Compiled);
    private static readonly Regex DateExpression = new(DatePattern, RegexOptions.Compiled);

    public static string GetNextSequenceNumber(Sequence sequence, IClock clock)
    {
        const char CustomYear = 'Y';
        const char CustomDay = 'D';
        const char CustomMonth = 'm';
        const char StandardYear = 'y';
        const char StandardDay = 'd';
        const char StandardMonth = 'M';
        const char Zero = '0';

        var number = GenerateNumber(sequence, clock);

        var date = clock.GetNow;
        var code = sequence.Pattern;

        var dateMatches = DateExpression.Matches(sequence.Pattern);
        for (var index = 0; index < dateMatches.Count; index++)
        {
            var match = dateMatches[index];
            var dateReplacement = match.Captures[0].Value;
            var dateFormat = match.Groups[1].Value;

            dateFormat = dateFormat.Replace(CustomYear, StandardYear);
            dateFormat = dateFormat.Replace(CustomDay, StandardDay);
            dateFormat = dateFormat.Replace(CustomMonth, StandardMonth);

            var formattedDate = date.ToString(dateFormat);

            code = code.Replace(dateReplacement, formattedDate);
        }

        var numberMatches = NumberExpression.Matches(sequence.Pattern);
        for (var index = 0; index < numberMatches.Count; index++)
        {
            var match = numberMatches[index];
            var numberReplacement = match.Captures[0].Value;
            var numberFormat = match.Groups[1].Value;

            numberFormat = string.Empty.PadRight(numberFormat.Length, Zero);
            var formattedNumber = number.ToString(numberFormat);

            code = code.Replace(numberReplacement, formattedNumber);
        }

        return code;
    }

    private static long GenerateNumber(Sequence sequence, IClock clock)
    {
        long number = 0;

        if (!sequence.LastIssueDate.HasValue)
        {
            number = sequence.Seed + sequence.Increment;
        }
        else
        {
            switch (sequence.Rollover)
            {
                case Rollover.Day:
                    if (sequence.LastIssueDate.HasValue)
                    {
                        if (sequence.LastIssueDate.Value.DateTime.IsSameDay(clock.GetNow.DateTime))
                        {
                            number = (sequence.LastIssueValue ?? sequence.Seed) + sequence.Increment;
                        }
                        else
                        {
                            number = sequence.Seed + sequence.Increment;
                        }
                    }
                    break;
                case Rollover.Month:
                    if (sequence.LastIssueDate.HasValue)
                    {
                        if (sequence.LastIssueDate.Value.DateTime.IsSameMonth(clock.GetNow.DateTime))
                        {
                            number = (sequence.LastIssueValue ?? sequence.Seed) + sequence.Increment;
                        }
                        else
                        {
                            number = sequence.Seed + sequence.Increment;
                        }
                    }
                    break;
                case Rollover.Year:
                    if (sequence.LastIssueDate.HasValue)
                    {
                        if (sequence.LastIssueDate.Value.DateTime.Year == clock.GetNow.Year)
                        {
                            number = (sequence.LastIssueValue ?? sequence.Seed) + sequence.Increment;
                        }
                        else
                        {
                            number = sequence.Seed + sequence.Increment;
                        }
                    }
                    break;
                case Rollover.Continuous:
                default:
                    number = (sequence.LastIssueValue ?? 0) + sequence.Increment;
                    break;
            }
        }

        sequence.LastIssueValue = number;
        sequence.LastIssueDate = clock.GetNow;

        return number;
    }
}