#nullable enable

using MBN_STOCK_WEBVIEW.DbWriteSmoke;

namespace MBN_STOCK_WEBVIEW.DbWriteSmoke.Tests;

public sealed class SmokeCommandLineTests
{
    [Fact]
    public async Task DefaultInvocation_DoesNotCreatePlanOrTouchDatabase()
    {
        var planCalls = 0;
        using var output = new StringWriter();

        var exitCode = await DevelopmentDatabaseWriteSmokeApp.RunAsync(
            [],
            _ =>
            {
                ++planCalls;
                throw new InvalidOperationException("must not load configuration");
            },
            output);

        Assert.Equal(DevelopmentDatabaseWriteSmokeApp.NotAcknowledgedExitCode, exitCode);
        Assert.Equal(0, planCalls);
        Assert.Contains("no database call", output.ToString(), StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public async Task ExactAcknowledgement_RunsPlanAndRedactsFailureDetails()
    {
        var secret = "SECRET_CREDENTIAL_OR_VALUE";
        var planCalls = 0;
        var scenario = Scenario(
            create: _ => Task.FromException(new UnknownTestException(secret)));
        using var output = new StringWriter();

        var exitCode = await DevelopmentDatabaseWriteSmokeApp.RunAsync(
            [SmokeCommandLine.WriteAcknowledgement],
            _ =>
            {
                ++planCalls;
                return [scenario];
            },
            output);

        Assert.Equal(DevelopmentDatabaseWriteSmokeApp.OutcomeUnknownExitCode, exitCode);
        Assert.Equal(1, planCalls);
        Assert.DoesNotContain(secret, output.ToString(), StringComparison.Ordinal);
        Assert.Contains("QUARANTINED", output.ToString(), StringComparison.Ordinal);
    }

    [Theory]
    [InlineData("--i-understand-this-writes-development-db")]
    [InlineData("--I-UNDERSTAND-THIS-WRITES-DEVELOPMENT-DATABASE")]
    [InlineData("--i-understand-this-writes-development-database --extra")]
    public async Task NearMissAcknowledgement_IsRejectedWithoutPlan(string commandLine)
    {
        var planCalls = 0;
        using var output = new StringWriter();

        var exitCode = await DevelopmentDatabaseWriteSmokeApp.RunAsync(
            commandLine.Split(' ', StringSplitOptions.RemoveEmptyEntries),
            _ =>
            {
                ++planCalls;
                return [];
            },
            output);

        Assert.Equal(DevelopmentDatabaseWriteSmokeApp.NotAcknowledgedExitCode, exitCode);
        Assert.Equal(0, planCalls);
    }

    [Fact]
    public async Task OutcomeUnknown_StopsAllLaterScenarios()
    {
        var laterCalls = 0;
        var unknown = Scenario(
            create: _ => Task.FromException(new UnknownTestException("hidden")));
        var later = Scenario(
            preflight: _ =>
            {
                ++laterCalls;
                return Task.CompletedTask;
            });
        using var output = new StringWriter();

        var exitCode = await DevelopmentDatabaseWriteSmokeApp.RunAsync(
            [SmokeCommandLine.WriteAcknowledgement],
            _ => [unknown, later],
            output);

        Assert.Equal(DevelopmentDatabaseWriteSmokeApp.OutcomeUnknownExitCode, exitCode);
        Assert.Equal(0, laterCalls);
    }

    private static DevelopmentDatabaseSmokeScenario Scenario(
        Func<CancellationToken, Task>? preflight = null,
        Func<CancellationToken, Task>? create = null) =>
        new(
            "test scenario",
            preflight ?? Completed,
            create ?? Completed,
            Completed,
            save: null,
            verifySave: null,
            Completed,
            Completed,
            exception => exception is UnknownTestException);

    private static Task Completed(CancellationToken _) => Task.CompletedTask;

    private sealed class UnknownTestException(string message) : Exception(message);
}
