using MMoneyCoderSharp.Data;

namespace MBN_STOCK_WEBVIEW.Core.Tests;

public sealed class LegacyOperatorCatalogSchemaValidationServiceTests
{
    [Fact]
    public async Task ValidateAsync_ExecutesOnlyZeroRowOracleAndMariaSchemaQueries()
    {
        var query = new CatalogRecordingQueryExecutor(call => call.Source switch
        {
            DataSourceKind.Oracle => OperatorCatalogTestTables.Empty(
                "THEME_CODE",
                "THEME_TITLE",
                "THEME_MARKET",
                "ITEM_THEME_CODE",
                "ITEM_CODE",
                "ITEM_NAME",
                "ITEM_INDEX",
                "EXPERT_CODE",
                "EXPERT_NAME",
                "RECOMMEND_EXPERT_CODE",
                "STOCK_CODE",
                "STOCK_NAME",
                "BUY_AMOUNT",
                "PLAY_INDEX"),
            DataSourceKind.MariaDb => OperatorCatalogTestTables.Empty(
                "THEME_CODE",
                "THEME_TITLE",
                "THEME_MARKET",
                "ITEM_THEME_CODE",
                "ITEM_CODE",
                "ITEM_NAME",
                "ITEM_INDEX"),
            _ => throw new InvalidOperationException()
        });
        var service = new LegacyOperatorCatalogSchemaValidationService(query);
        using var cancellation = new CancellationTokenSource();

        var result = await service.ValidateAsync(cancellation.Token);

        Assert.Equal(
            [DataSourceKind.Oracle, DataSourceKind.MariaDb],
            result.ValidatedSources);
        Assert.Collection(
            query.Calls,
            call =>
            {
                Assert.Equal(DataSourceKind.Oracle, call.Source);
                Assert.Equal(
                    LegacyOperatorCatalogSchemaValidationService.OracleQueryName,
                    call.TableName);
                Assert.Contains("EXPERT_LIST", call.Spec.Sql, StringComparison.Ordinal);
                Assert.Contains("RECOMMEND_LIST", call.Spec.Sql, StringComparison.Ordinal);
                Assert.Contains("WHERE 1 = 0", call.Spec.Sql, StringComparison.Ordinal);
                Assert.Empty(call.Spec.Parameters);
                Assert.Equal(cancellation.Token, call.CancellationToken);
            },
            call =>
            {
                Assert.Equal(DataSourceKind.MariaDb, call.Source);
                Assert.Equal(
                    LegacyOperatorCatalogSchemaValidationService.MariaDbQueryName,
                    call.TableName);
                Assert.Contains("SB_LIST", call.Spec.Sql, StringComparison.Ordinal);
                Assert.Contains("SB_ITEM", call.Spec.Sql, StringComparison.Ordinal);
                Assert.Contains("WHERE 1 = 0", call.Spec.Sql, StringComparison.Ordinal);
                Assert.Empty(call.Spec.Parameters);
                Assert.Equal(cancellation.Token, call.CancellationToken);
            });
    }

    [Fact]
    public async Task ValidateAsync_FailsClosedOnUnexpectedSchemaAndStopsBeforeSecondSource()
    {
        var query = new CatalogRecordingQueryExecutor(_ =>
            OperatorCatalogTestTables.Empty("WRONG_COLUMN"));
        var service = new LegacyOperatorCatalogSchemaValidationService(query);

        var exception = await Assert.ThrowsAsync<OperatorCatalogSchemaException>(() =>
            service.ValidateAsync());

        Assert.Equal(DataSourceKind.Oracle, exception.DataSource);
        Assert.Single(query.Calls);
    }

    [Fact]
    public async Task ValidateAsync_PropagatesCancellationWithoutAdditionalQuery()
    {
        using var cancellation = new CancellationTokenSource();
        cancellation.Cancel();
        var query = new CatalogRecordingQueryExecutor(_ =>
            throw new InvalidOperationException("No query expected."));
        var service = new LegacyOperatorCatalogSchemaValidationService(query);

        await Assert.ThrowsAnyAsync<OperationCanceledException>(() =>
            service.ValidateAsync(cancellation.Token));

        Assert.Empty(query.Calls);
    }
}
