#nullable enable

namespace MBN_STOCK_WEBVIEW.DbWriteSmoke;

internal static class DevelopmentDatabaseWriteSmokeApp
{
    internal const int SuccessExitCode = 0;
    internal const int FailureExitCode = 1;
    internal const int NotAcknowledgedExitCode = 2;
    internal const int OutcomeUnknownExitCode = 3;

    internal static async Task<int> RunAsync(
        IReadOnlyList<string> arguments,
        Func<string?, IReadOnlyList<DevelopmentDatabaseSmokeScenario>> createPlan,
        TextWriter output,
        CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(arguments);
        ArgumentNullException.ThrowIfNull(createPlan);
        ArgumentNullException.ThrowIfNull(output);

        if (!SmokeCommandLine.TryParse(arguments, out var options))
        {
            await output.WriteLineAsync(SmokeCommandLine.Usage).ConfigureAwait(false);
            return NotAcknowledgedExitCode;
        }

        if (options.ShowHelp)
        {
            await output.WriteLineAsync(SmokeCommandLine.Usage).ConfigureAwait(false);
            await output.WriteLineAsync(
                "No write occurs unless the exact development-database acknowledgement is present.")
                .ConfigureAwait(false);
            return SuccessExitCode;
        }

        if (!options.IsWriteAcknowledged)
        {
            await output.WriteLineAsync(
                "BLOCKED: development database writes were not acknowledged; no configuration was loaded and no database call was made.")
                .ConfigureAwait(false);
            await output.WriteLineAsync(SmokeCommandLine.Usage).ConfigureAwait(false);
            return NotAcknowledgedExitCode;
        }

        IReadOnlyList<DevelopmentDatabaseSmokeScenario> plan;
        try
        {
            plan = createPlan(options.ConfigurationPath);
        }
        catch
        {
            await output.WriteLineAsync(
                "FAIL: configuration or smoke-plan creation failed (details redacted).")
                .ConfigureAwait(false);
            return FailureExitCode;
        }

        await output.WriteLineAsync(
            "START: isolated development DB write/readback/cleanup smoke; credentials, SQL, identifiers, and values are redacted.")
            .ConfigureAwait(false);

        foreach (var scenario in plan)
        {
            cancellationToken.ThrowIfCancellationRequested();
            await output.WriteLineAsync("RUN: " + scenario.Name).ConfigureAwait(false);

            DevelopmentDatabaseSmokeResult result;
            try
            {
                result = await scenario.RunAsync(cancellationToken).ConfigureAwait(false);
            }
            catch
            {
                await output.WriteLineAsync(
                    "FAIL: unclassified smoke failure; no retry was attempted (details redacted).")
                    .ConfigureAwait(false);
                return FailureExitCode;
            }

            if (result.Passed)
            {
                await output.WriteLineAsync(
                    "PASS: create/save, fresh readback, cleanup, and absence verification completed.")
                    .ConfigureAwait(false);
                continue;
            }

            await output.WriteLineAsync(
                $"FAIL: stage={result.FailureStage}; failureClass={result.FailureClass}; cleanupAttempted={result.CleanupAttempted}; cleanupVerified={result.CleanupVerified}; details redacted.")
                .ConfigureAwait(false);
            if (result.OutcomeUnknown)
            {
                await output.WriteLineAsync(
                    "QUARANTINED: mutation outcome is unknown; no retry, cleanup mutation, or later scenario was issued. A uniquely prefixed record may require manual inspection.")
                    .ConfigureAwait(false);
                return OutcomeUnknownExitCode;
            }

            return FailureExitCode;
        }

        await output.WriteLineAsync(
            "PASS: every development DB write scenario was verified absent after cleanup.")
            .ConfigureAwait(false);
        return SuccessExitCode;
    }
}
