using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using TakeoutSaaS.Infrastructure.Common.Options;
using TakeoutSaaS.Infrastructure.Common.Persistence;
using TakeoutSaaS.Shared.Abstractions.Data;
using TakeoutSaaS.Shared.Abstractions.Ids;
using TakeoutSaaS.Shared.Kernel.Ids;

namespace TakeoutSaaS.Infrastructure.Common.Extensions;

/// <summary>
/// 数据访问与多数据源相关的服务注册扩展。
/// </summary>
public static class DatabaseServiceCollectionExtensions
{
    /// <summary>
    /// 注册数据库基础设施（多数据源配置、读写分离、Dapper 执行器）。
    /// </summary>
    /// <param name="services">服务集合。</param>
    /// <param name="configuration">配置源。</param>
    /// <returns>服务集合。</returns>
    public static IServiceCollection AddDatabaseInfrastructure(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddOptions<DatabaseOptions>()
            .Bind(configuration.GetSection(DatabaseOptions.SectionName))
            .ValidateDataAnnotations()
            .ValidateOnStart();

        services.AddOptions<IdGeneratorOptions>()
            .Bind(configuration.GetSection(IdGeneratorOptions.SectionName))
            .ValidateDataAnnotations()
            .ValidateOnStart();

        services.AddSingleton<IIdGenerator>(sp =>
        {
            var options = sp.GetRequiredService<Microsoft.Extensions.Options.IOptions<IdGeneratorOptions>>().Value;
            return new SnowflakeIdGenerator(options.WorkerId, options.DatacenterId);
        });

        services.AddSingleton<IDatabaseConnectionFactory, DatabaseConnectionFactory>();
        services.AddScoped<IDapperExecutor, DapperExecutor>();
        return services;
    }

    /// <summary>
    /// 为指定 DbContext 注册读写分离的 PostgreSQL 配置，同时提供读上下文工厂。
    /// </summary>
    /// <typeparam name="TContext">上下文类型。</typeparam>
    /// <param name="services">服务集合。</param>
    /// <param name="dataSourceName">逻辑数据源名称。</param>
    /// <returns>服务集合。</returns>
    public static IServiceCollection AddPostgresDbContext<TContext>(
        this IServiceCollection services,
        string dataSourceName)
        where TContext : DbContext
    {
        services.AddDbContext<TContext>(
            (sp, options) =>
            {
                ConfigureDbContextOptions(sp, options, dataSourceName, DatabaseConnectionRole.Write);
            },
            contextLifetime: ServiceLifetime.Scoped,
            optionsLifetime: ServiceLifetime.Singleton);

        services.AddDbContextFactory<TContext>((sp, options) =>
        {
            ConfigureDbContextOptions(sp, options, dataSourceName, DatabaseConnectionRole.Read);
        });

        return services;
    }

    /// <summary>
    /// 配置 DbContextOptions，应用连接串、命令超时与重试策略。
    /// </summary>
    /// <param name="serviceProvider">服务提供程序。</param>
    /// <param name="optionsBuilder">上下文配置器。</param>
    /// <param name="dataSourceName">数据源名称。</param>
    /// <param name="role">连接角色。</param>
    private static void ConfigureDbContextOptions(
        IServiceProvider serviceProvider,
        DbContextOptionsBuilder optionsBuilder,
        string dataSourceName,
        DatabaseConnectionRole role)
    {
        var connection = serviceProvider
            .GetRequiredService<IDatabaseConnectionFactory>()
            .GetConnection(dataSourceName, role);

        optionsBuilder.UseNpgsql(
            connection.ConnectionString,
            npgsqlOptions =>
            {
                npgsqlOptions.CommandTimeout(connection.CommandTimeoutSeconds);
                npgsqlOptions.EnableRetryOnFailure(
                    connection.MaxRetryCount,
                    TimeSpan.FromSeconds(connection.MaxRetryDelaySeconds),
                    null);
            });
    }
}
