#nullable enable

using System.Text.RegularExpressions;
using MBN_STOCK_WEBVIEW.Infrastructure;
using MMoneyCoderSharp.Data;

namespace MBN_STOCK_WEBVIEW.DbWriteSmoke;

internal static partial class OwnedManualSmokeResidueRecovery
{
    internal const string RecoveryOption = "--reconcile-known-owned-manual-residue";

    private static readonly IReadOnlyDictionary<ManualFinancialScreenKind, char> ScenarioCodes =
        new Dictionary<ManualFinancialScreenKind, char>
        {
            [ManualFinancialScreenKind.RevenueComposition] = 'P',
            [ManualFinancialScreenKind.GrowthMetrics] = 'G',
            [ManualFinancialScreenKind.Sales] = 'S',
            [ManualFinancialScreenKind.OperatingProfit] = 'O'
        };

    internal static bool IsRequested(IReadOnlyList<string> arguments) =>
        arguments.Contains(RecoveryOption, StringComparer.Ordinal);

    internal static async Task<int> RunAsync(
        IReadOnlyList<string> arguments,
        TextWriter output,
        CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(arguments);
        ArgumentNullException.ThrowIfNull(output);
        if (!TryParse(arguments, out var configurationPath))
        {
            await output.WriteLineAsync(
                "Usage: MBN_STOCK_WEBVIEW.DbWriteSmoke " + RecoveryOption + " " +
                SmokeCommandLine.WriteAcknowledgement +
                " [" + SmokeCommandLine.ConfigurationOption + " <database.local.json>]")
                .ConfigureAwait(false);
            return DevelopmentDatabaseWriteSmokeApp.NotAcknowledgedExitCode;
        }

        try
        {
            var options = new DatabaseOptionsLoader().Load(configurationPath);
            options.Resilience.MaximumRetryCount = 0;
            options.Resilience.InitialRetryDelayMilliseconds = 0;
            var runtime = DatabaseRuntime.Create(options);
            var service = new LegacyManualFinancialScreenService(
                runtime.Executor,
                new OracleManualFinancialMutationExecutor(
                    runtime.ConnectionFactory,
                    options.Resilience));
            var prefix = SmokeRunIdentity.Prefix +
                DateTimeOffset.UtcNow.ToString("yyMMdd", System.Globalization.CultureInfo.InvariantCulture);
            var total = 0;

            await output.WriteLineAsync(
                "START: reconcile only the closed, current-UTC-date manual smoke namespace; details redacted.")
                .ConfigureAwait(false);
            foreach (var (screen, scenarioCode) in ScenarioCodes)
            {
                var result = await service.SearchAsync(
                        screen,
                        prefix,
                        LegacyManualFinancialScreenService.MaximumResults,
                        cancellationToken)
                    .ConfigureAwait(false);
                var owned = result.Items.Where(item =>
                        IsOwned(item.Record.Identity.StockName, prefix, scenarioCode))
                    .ToArray();
                if (result.IsTruncated || owned.Length > 1)
                {
                    await output.WriteLineAsync(
                        "BLOCKED: the owned residue set was not singular and complete; writesAttempted=false.")
                        .ConfigureAwait(false);
                    return DevelopmentDatabaseWriteSmokeApp.FailureExitCode;
                }

                if (owned.Length == 0)
                {
                    continue;
                }

                await service.DeleteAsync(owned[0], cancellationToken).ConfigureAwait(false);
                total++;
                try
                {
                    _ = await service.GetAsync(
                            owned[0].Record.Identity,
                            cancellationToken)
                        .ConfigureAwait(false);
                }
                catch (ManualFinancialNotFoundException)
                {
                    continue;
                }

                await output.WriteLineAsync(
                    "FAIL: an owned residue remained after one delete; no retry was issued.")
                    .ConfigureAwait(false);
                return DevelopmentDatabaseWriteSmokeApp.FailureExitCode;
            }

            await output.WriteLineAsync(
                $"PASS: reconciled={total}; every owned residue is absent; no retry was issued.")
                .ConfigureAwait(false);
            return DevelopmentDatabaseWriteSmokeApp.SuccessExitCode;
        }
        catch (ManualFinancialMutationException exception)
        {
            await output.WriteLineAsync(
                "FAIL: failureClass=" + RedactedDatabaseFailureClassifier.Classify(exception) +
                "; outcomeUnknown=" + exception.OutcomeUnknown.ToString().ToLowerInvariant() +
                "; no retry was issued.")
                .ConfigureAwait(false);
            return exception.OutcomeUnknown
                ? DevelopmentDatabaseWriteSmokeApp.OutcomeUnknownExitCode
                : DevelopmentDatabaseWriteSmokeApp.FailureExitCode;
        }
        catch (Exception exception)
        {
            await output.WriteLineAsync(
                "FAIL: failureClass=" + RedactedDatabaseFailureClassifier.Classify(exception) +
                "; no retry was issued; details redacted.")
                .ConfigureAwait(false);
            return DevelopmentDatabaseWriteSmokeApp.FailureExitCode;
        }
    }

    private static bool IsOwned(string value, string prefix, char scenarioCode) =>
        value.Length == 20 &&
        value.StartsWith(prefix, StringComparison.Ordinal) &&
        value[^1] == scenarioCode &&
        OwnedNamePattern().IsMatch(value);

    private static bool TryParse(
        IReadOnlyList<string> arguments,
        out string? configurationPath)
    {
        configurationPath = null;
        var recovery = false;
        var acknowledged = false;
        for (var index = 0; index < arguments.Count; index++)
        {
            switch (arguments[index])
            {
                case RecoveryOption when !recovery:
                    recovery = true;
                    break;
                case SmokeCommandLine.WriteAcknowledgement when !acknowledged:
                    acknowledged = true;
                    break;
                case SmokeCommandLine.ConfigurationOption when configurationPath is null &&
                    index + 1 < arguments.Count:
                    configurationPath = arguments[++index];
                    if (string.IsNullOrWhiteSpace(configurationPath))
                    {
                        return false;
                    }

                    break;
                default:
                    return false;
            }
        }

        return recovery && acknowledged;
    }

    [GeneratedRegex("^MBW[0-9]{6}[0-9A-F]{10}[PGSO]$", RegexOptions.CultureInvariant)]
    private static partial Regex OwnedNamePattern();
}
