using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;
using TakeoutSaaS.Infrastructure.Common.Options;
using TakeoutSaaS.Shared.Abstractions.Data;

namespace TakeoutSaaS.Infrastructure.Common.Persistence;

/// <summary>
/// 数据库连接工厂，支持读写分离及连接配置校验。
/// </summary>
public sealed class DatabaseConnectionFactory(
    IOptionsMonitor<DatabaseOptions> optionsMonitor,
    IConfiguration configuration,
    ILogger<DatabaseConnectionFactory> logger) : IDatabaseConnectionFactory
{
    private const int DefaultCommandTimeoutSeconds = 30;
    private const int DefaultMaxRetryCount = 3;
    private const int DefaultMaxRetryDelaySeconds = 5;

    /// <summary>
    /// 获取指定数据源与读写角色的连接信息。
    /// </summary>
    /// <param name="dataSourceName">逻辑数据源名称。</param>
    /// <param name="role">连接角色。</param>
    /// <returns>连接串与超时/重试配置。</returns>
    public DatabaseConnectionDetails GetConnection(string dataSourceName, DatabaseConnectionRole role)
    {
        if (string.IsNullOrWhiteSpace(dataSourceName))
        {
            logger.LogWarning("请求的数据源名称为空，使用默认连接。");
            return BuildFallbackConnection();
        }

        var options = optionsMonitor.CurrentValue.Find(dataSourceName);
        if (options != null)
        {
            if (!ValidateOptions(dataSourceName, options))
            {
                return BuildFallbackConnection();
            }

            var connectionString = ResolveConnectionString(options, role);
            return new DatabaseConnectionDetails(
                connectionString,
                options.CommandTimeoutSeconds,
                options.MaxRetryCount,
                options.MaxRetryDelaySeconds);
        }

        var fallback = configuration.GetConnectionString(dataSourceName);
        if (string.IsNullOrWhiteSpace(fallback))
        {
            logger.LogError("缺少数据源 {DataSource} 的连接配置，回退到默认本地连接。", dataSourceName);
            return BuildFallbackConnection();
        }

        logger.LogWarning("未找到数据源 {DataSource} 的 Database 节配置，回退使用 ConnectionStrings。", dataSourceName);
        return new DatabaseConnectionDetails(
            fallback,
            DefaultCommandTimeoutSeconds,
            DefaultMaxRetryCount,
            DefaultMaxRetryDelaySeconds);
    }

    /// <summary>
    /// 校验数据源配置完整性。
    /// </summary>
    /// <param name="dataSourceName">数据源名称。</param>
    /// <param name="options">数据源配置。</param>
    /// <exception cref="InvalidOperationException">配置不合法时抛出。</exception>
    private bool ValidateOptions(string dataSourceName, DatabaseDataSourceOptions options)
    {
        var results = new List<ValidationResult>();
        var context = new ValidationContext(options);
        if (!Validator.TryValidateObject(options, context, results, validateAllProperties: true))
        {
            var errorMessages = string.Join("; ", results.Select(result => result.ErrorMessage));
            logger.LogError("数据源 {DataSource} 配置非法：{Errors}，回退到默认连接。", dataSourceName, errorMessages);
            return false;
        }

        return true;
    }

    /// <summary>
    /// 根据读写角色选择连接串，从读连接随机分配。
    /// </summary>
    /// <param name="options">数据源配置。</param>
    /// <param name="role">连接角色。</param>
    /// <returns>可用连接串。</returns>
    private string ResolveConnectionString(DatabaseDataSourceOptions options, DatabaseConnectionRole role)
    {
        if (role == DatabaseConnectionRole.Read && options.Reads.Count > 0)
        {
            var index = RandomNumberGenerator.GetInt32(options.Reads.Count);
            return options.Reads[index];
        }

        if (string.IsNullOrWhiteSpace(options.Write))
        {
            return BuildFallbackConnection().ConnectionString;
        }

        return options.Write;
    }

    private DatabaseConnectionDetails BuildFallbackConnection()
    {
        const string fallback = "Host=120.53.222.17;Port=5432;Database=postgres;Username=postgres;Password=MsuMshk112233;Pooling=true;Minimum Pool Size=1;Maximum Pool Size=20";
        logger.LogWarning("使用默认回退连接串：{Connection}", fallback);
        return new DatabaseConnectionDetails(
            fallback,
            DefaultCommandTimeoutSeconds,
            DefaultMaxRetryCount,
            DefaultMaxRetryDelaySeconds);
    }
}
