#nullable enable

using System.Data;
using MMoneyCoderSharp.Data;

namespace MBN_STOCK_WEBVIEW.DbWriteSmoke;

/// <summary>
/// The legacy deletion workflows remove child rows by numeric parent key. This
/// read-only guard proves a newly suggested key has no pre-existing orphaned
/// children before a smoke record can adopt that key.
/// </summary>
internal sealed class ExistingChildRowGuard
{
    private const string ResultColumn = "ROW_COUNT";

    private readonly IDataQueryExecutor _executor;

    internal ExistingChildRowGuard(IDataQueryExecutor executor)
    {
        _executor = executor ?? throw new ArgumentNullException(nameof(executor));
    }

    internal Task EnsureNamedPlaylistChildrenAbsentAsync(
        string programCode,
        CancellationToken cancellationToken) =>
        EnsureZeroAsync(
            DataSourceKind.Oracle,
            "DB_WRITE_SMOKE_PLAYLIST_CHILD_PREFLIGHT",
            "SELECT TO_CHAR(COUNT(*)) ROW_COUNT FROM PLAY_LIST WHERE PG_CODE = :parent_code",
            programCode,
            cancellationToken);

    internal Task EnsureThemeChildrenAbsentAsync(
        ThemeMarket market,
        string themeCode,
        CancellationToken cancellationToken) =>
        market switch
        {
            ThemeMarket.Krx => EnsureZeroAsync(
                DataSourceKind.Oracle,
                "DB_WRITE_SMOKE_KRX_THEME_CHILD_PREFLIGHT",
                "SELECT TO_CHAR(COUNT(*)) ROW_COUNT FROM SB_ITEM WHERE SB_M_CODE = :parent_code",
                themeCode,
                cancellationToken),
            ThemeMarket.Nxt => EnsureZeroAsync(
                DataSourceKind.MariaDb,
                "DB_WRITE_SMOKE_NXT_THEME_CHILD_PREFLIGHT",
                "SELECT CAST(COUNT(*) AS CHAR) ROW_COUNT FROM SB_ITEM WHERE SB_M_CODE = @parent_code",
                themeCode,
                cancellationToken),
            _ => throw new ArgumentOutOfRangeException(nameof(market))
        };

    internal Task EnsureExpertChildrenAbsentAsync(
        string expertCode,
        CancellationToken cancellationToken) =>
        EnsureZeroAsync(
            DataSourceKind.Oracle,
            "DB_WRITE_SMOKE_EXPERT_CHILD_PREFLIGHT",
            "SELECT TO_CHAR(COUNT(*)) ROW_COUNT FROM RECOMMEND_LIST WHERE RC_CODE = :parent_code",
            expertCode,
            cancellationToken);

    private async Task EnsureZeroAsync(
        DataSourceKind source,
        string queryName,
        string sql,
        string parentCode,
        CancellationToken cancellationToken)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(parentCode);
        var spec = new DataQuerySpec(
            sql,
            [new DataQueryParameter("parent_code", parentCode, DbType.String)]);
        spec.ValidateFor(source);
        var table = await _executor.ExecuteAsync(
            source,
            queryName,
            spec,
            cancellationToken).ConfigureAwait(false);

        if (table is null ||
            table.Columns.Count != 1 ||
            table.Rows.Count != 1 ||
            !string.Equals(
                table.Columns[0].ColumnName,
                ResultColumn,
                StringComparison.Ordinal) ||
            table.Columns[0].DataType != typeof(string) ||
            table.Rows[0][0] is not string count ||
            !string.Equals(count, "0", StringComparison.Ordinal))
        {
            throw new InvalidOperationException(
                "A suggested numeric key has existing child rows or an invalid preflight result.");
        }
    }
}
