using Microsoft.Extensions.Logging;
using Npgsql;
using System.Data;
using TakeoutSaaS.Shared.Abstractions.Data;

namespace TakeoutSaaS.Infrastructure.Common.Persistence;

/// <summary>
/// 基于 Dapper 的执行器实现，封装连接创建与读写分离。
/// </summary>
public sealed class DapperExecutor(
    IDatabaseConnectionFactory connectionFactory,
    ILogger<DapperExecutor> logger) : IDapperExecutor
{
    /// <summary>
    /// 使用指定数据源与读写角色执行异步查询。
    /// </summary>
    public async Task<TResult> QueryAsync<TResult>(
        string dataSourceName,
        DatabaseConnectionRole role,
        Func<IDbConnection, CancellationToken, Task<TResult>> query,
        CancellationToken cancellationToken = default)
    {
        return await ExecuteAsync(
            dataSourceName,
            role,
            async (connection, token) => await query(connection, token),
            cancellationToken);
    }

    /// <summary>
    /// 使用指定数据源与读写角色执行异步命令。
    /// </summary>
    public async Task ExecuteAsync(
        string dataSourceName,
        DatabaseConnectionRole role,
        Func<IDbConnection, CancellationToken, Task> command,
        CancellationToken cancellationToken = default)
    {
        await ExecuteAsync(
            dataSourceName,
            role,
            async (connection, token) =>
            {
                await command(connection, token);
                return true;
            },
            cancellationToken);
    }

    /// <summary>
    /// 获取默认命令超时时间（秒）。
    /// </summary>
    public int GetDefaultCommandTimeoutSeconds(string dataSourceName, DatabaseConnectionRole role = DatabaseConnectionRole.Read)
    {
        var details = connectionFactory.GetConnection(dataSourceName, role);
        return details.CommandTimeoutSeconds;
    }

    /// <summary>
    /// 核心执行逻辑：创建连接、打开并执行委托。
    /// </summary>
    private async Task<TResult> ExecuteAsync<TResult>(
        string dataSourceName,
        DatabaseConnectionRole role,
        Func<IDbConnection, CancellationToken, Task<TResult>> action,
        CancellationToken cancellationToken)
    {
        var details = connectionFactory.GetConnection(dataSourceName, role);
        await using var connection = new NpgsqlConnection(details.ConnectionString);

        logger.LogDebug(
            "打开数据库连接：DataSource={DataSource} Role={Role}",
            dataSourceName,
            role);

        await connection.OpenAsync(cancellationToken);
        return await action(connection, cancellationToken);
    }
}
