using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;

namespace NPin.Framework.TenantManagement.Domain;

/// <summary>
/// 数据库连接字符串解析器，替代原有
/// </summary>
[Dependency(ReplaceServices = true)]
public class NPinMultiTenantConnectionStringResolver : DefaultConnectionStringResolver
{
    private readonly ICurrentTenant _currentTenant;
    private readonly IServiceProvider _serviceProvider;

    public NPinMultiTenantConnectionStringResolver(IOptionsMonitor<AbpDbConnectionOptions> options,
        ICurrentTenant currentTenant, IServiceProvider serviceProvider) : base(options)
    {
        _currentTenant = currentTenant;
        _serviceProvider = serviceProvider;
    }

    public override async Task<string> ResolveAsync(string? connectionStringName = null)
    {
        if (_currentTenant.Id == null)
        {
            // 无租户信息，使用原有解析器
            return await base.ResolveAsync(connectionStringName);
        }

        // 通过Store寻找租户缓存
        var tenant = await FindTenantConfigurationAsync(_currentTenant.Id.Value);
        if (tenant == null || tenant.ConnectionStrings.IsNullOrEmpty())
        {
            // 找不到租户缓存，使用原有解析器
            return await base.ResolveAsync(connectionStringName);
        }

        var tenantDefaultConnectionString = tenant.ConnectionStrings?.Default;

        var connString = tenant.ConnectionStrings?.FirstOrDefault().Value;
        if (!connString.IsNullOrWhiteSpace())
        {
            return connString;
        }

        // 库隔离
        var database = Options.Databases.GetMappedDatabaseOrNull(connectionStringName);
        if (database is { IsUsedByTenants: true })
        {
            connString = tenant.ConnectionStrings?.GetOrDefault(database.DatabaseName);
            if (!connString.IsNullOrWhiteSpace())
            {
                return connString;
            }
        }

        if (!tenantDefaultConnectionString.IsNullOrWhiteSpace())
        {
            return tenantDefaultConnectionString;
        }

        return await base.ResolveAsync(connectionStringName);
    }

    protected virtual async Task<TenantConfiguration?> FindTenantConfigurationAsync(Guid tenantId)
    {
        using var serviceScope = _serviceProvider.CreateScope();

        var tenantStore = serviceScope
            .ServiceProvider
            .GetRequiredService<ITenantStore>();

        return await tenantStore.FindAsync(tenantId);
    }

    [Obsolete("Use ResolveAsync Instead.")]
    public override string Resolve(string? connectionStringName = null)
    {
        if (_currentTenant.Id == null)
        {
            // 无租户信息，使用原有解析器
            return base.Resolve(connectionStringName);
        }

        // 通过Store寻找租户缓存
        var tenant = FindTenantConfiguration(_currentTenant.Id.Value);
        if (tenant == null || tenant.ConnectionStrings.IsNullOrEmpty())
        {
            // 找不到租户缓存，使用原有解析器
            return base.Resolve(connectionStringName);
        }

        var tenantDefaultConnectionString = tenant.ConnectionStrings?.Default;

        // Requesting default connection string...
        if (connectionStringName == null ||
            connectionStringName == ConnectionStrings.DefaultConnectionStringName)
        {
            //Return tenant's default or global default
            return !tenantDefaultConnectionString.IsNullOrWhiteSpace()
                ? tenantDefaultConnectionString!
                : Options.ConnectionStrings.Default!;
        }

        // Requesting specific connection string...
        var connString = tenant.ConnectionStrings?.GetOrDefault(connectionStringName);
        if (!connString.IsNullOrWhiteSpace())
        {
            // Found for the tenant
            return connString!;
        }

        // Fallback to tenant's default connection string if available
        if (!tenantDefaultConnectionString.IsNullOrWhiteSpace())
        {
            return tenantDefaultConnectionString!;
        }

        // Try to find the specific connection string for given name
        var connStringInOptions = Options.ConnectionStrings.GetOrDefault(connectionStringName);
        if (!connStringInOptions.IsNullOrWhiteSpace())
        {
            return connStringInOptions!;
        }

        // Fallback to the global default connection string
        var defaultConnectionString = Options.ConnectionStrings.Default;
        if (!defaultConnectionString.IsNullOrWhiteSpace())
        {
            return defaultConnectionString!;
        }

        throw new AbpException("No connection string defined!");
    }


    [Obsolete("Use FindTenantConfigurationAsync Instead.")]
    protected virtual TenantConfiguration? FindTenantConfiguration(Guid tenantId)
    {
        using var serviceScope = _serviceProvider.CreateScope();

        var tenantStore = serviceScope
            .ServiceProvider
            .GetRequiredService<ITenantStore>();

        return tenantStore.Find(tenantId);
    }
}