#nullable enable

using System.Data;
using MBN_STOCK_WEBVIEW.DbWriteSmoke;
using MMoneyCoderSharp.Data;

namespace MBN_STOCK_WEBVIEW.DbWriteSmoke.Tests;

public sealed class ExistingChildRowGuardTests
{
    [Fact]
    public async Task ZeroCount_UsesBoundProductionSourceSpecificPreflights()
    {
        var executor = new RecordingExecutor("0");
        var guard = new ExistingChildRowGuard(executor);

        await guard.EnsureNamedPlaylistChildrenAbsentAsync("00000042", default);
        await guard.EnsureThemeChildrenAbsentAsync(ThemeMarket.Krx, "00000043", default);
        await guard.EnsureThemeChildrenAbsentAsync(ThemeMarket.Nxt, "00000044", default);
        await guard.EnsureExpertChildrenAbsentAsync("0045", default);

        Assert.Equal(4, executor.Calls.Count);
        Assert.Equal(
            [DataSourceKind.Oracle, DataSourceKind.Oracle, DataSourceKind.MariaDb, DataSourceKind.Oracle],
            executor.Calls.Select(call => call.Source));
        Assert.All(executor.Calls, call =>
        {
            var parameter = Assert.Single(call.Spec.Parameters);
            Assert.Equal("parent_code", parameter.Name);
            Assert.Equal(DbType.String, parameter.DbType);
            Assert.DoesNotContain((string)parameter.Value!, call.Spec.Sql, StringComparison.Ordinal);
        });
    }

    [Fact]
    public async Task ExistingOrInvalidChildCount_FailsClosedBeforeMutation()
    {
        var guard = new ExistingChildRowGuard(new RecordingExecutor("1"));

        await Assert.ThrowsAsync<InvalidOperationException>(() =>
            guard.EnsureNamedPlaylistChildrenAbsentAsync("00000042", default));
    }

    private sealed class RecordingExecutor(string count) : IDataQueryExecutor
    {
        public List<QueryCall> Calls { get; } = [];

        public DataTable Execute(DataSourceKind source, string tableName, string query) =>
            throw new NotSupportedException();

        public Task<DataTable> ExecuteAsync(
            DataSourceKind source,
            string tableName,
            string query,
            CancellationToken cancellationToken = default) =>
            throw new NotSupportedException();

        public Task<DataTable> ExecuteAsync(
            DataSourceKind source,
            string tableName,
            DataQuerySpec query,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();
            Calls.Add(new QueryCall(source, tableName, query));
            var table = new DataTable();
            table.Columns.Add("ROW_COUNT", typeof(string));
            table.Rows.Add(count);
            return Task.FromResult(table);
        }
    }

    private sealed record QueryCall(
        DataSourceKind Source,
        string QueryName,
        DataQuerySpec Spec);
}
