using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using TakeoutSaaS.Infrastructure.Common.Options;
using TakeoutSaaS.Shared.Abstractions.Security;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Infrastructure.Common.Persistence.DesignTime;

/// <summary>
/// EF Core 设计时 DbContext 工厂基类，统一读取 appsettings 中的数据库配置。
/// </summary>
internal abstract class DesignTimeDbContextFactoryBase<TContext> : IDesignTimeDbContextFactory<TContext>
    where TContext : TenantAwareDbContext
{
    private readonly string _dataSourceName;
    private readonly string? _connectionStringEnvVar;

    /// <summary>
    /// 初始化设计时工厂基类。
    /// </summary>
    /// <param name="dataSourceName">数据源名称。</param>
    /// <param name="connectionStringEnvVar">连接串环境变量名。</param>
    protected DesignTimeDbContextFactoryBase(string dataSourceName, string? connectionStringEnvVar = null)
    {
        if (string.IsNullOrWhiteSpace(dataSourceName))
        {
            throw new ArgumentException("数据源名称不能为空。", nameof(dataSourceName));
        }

        _dataSourceName = dataSourceName;
        _connectionStringEnvVar = connectionStringEnvVar;
    }

    /// <summary>
    /// 创建设计时 DbContext。
    /// </summary>
    /// <param name="args">命令行参数。</param>
    /// <returns>DbContext 实例。</returns>
    public TContext CreateDbContext(string[] args)
    {
        // 1. 构建 DbContextOptions
        var optionsBuilder = new DbContextOptionsBuilder<TContext>();
        optionsBuilder.UseNpgsql(
            ResolveConnectionString(),
            npgsql =>
            {
                npgsql.CommandTimeout(30);
                npgsql.EnableRetryOnFailure();
            });

        // 2. 创建上下文
        return CreateContext(
            optionsBuilder.Options,
            new DesignTimeTenantProvider(),
            new DesignTimeCurrentUserAccessor());
    }

    /// <summary>
    /// 由子类实现的上下文工厂方法。
    /// </summary>
    /// <param name="options">上下文选项。</param>
    /// <param name="tenantProvider">租户提供器。</param>
    /// <param name="currentUserAccessor">当前用户访问器。</param>
    /// <returns>DbContext 实例。</returns>
    protected abstract TContext CreateContext(
        DbContextOptions<TContext> options,
        ITenantProvider tenantProvider,
        ICurrentUserAccessor currentUserAccessor);

    private string ResolveConnectionString()
    {
        if (!string.IsNullOrWhiteSpace(_connectionStringEnvVar))
        {
            var envValue = Environment.GetEnvironmentVariable(_connectionStringEnvVar);
            if (!string.IsNullOrWhiteSpace(envValue))
            {
                return envValue;
            }
        }

        var configuration = BuildConfiguration();
        var writeConnection = configuration[$"{DatabaseOptions.SectionName}:DataSources:{_dataSourceName}:Write"];
        if (string.IsNullOrWhiteSpace(writeConnection))
        {
            throw new InvalidOperationException(
                $"未在配置中找到数据源 '{_dataSourceName}' 的 Write 连接字符串，请检查 appsettings 或设置 {_connectionStringEnvVar ?? "相应"} 环境变量。");
        }

        return writeConnection;
    }

    private static IConfigurationRoot BuildConfiguration()
    {
        var basePath = ResolveConfigurationDirectory();
        var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development";

        return new ConfigurationBuilder()
            .SetBasePath(basePath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
            .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: false)
            .AddEnvironmentVariables()
            .Build();
    }

    private static string ResolveConfigurationDirectory()
    {
        var explicitDir = Environment.GetEnvironmentVariable("TAKEOUTSAAS_APPSETTINGS_DIR");
        if (!string.IsNullOrWhiteSpace(explicitDir) && Directory.Exists(explicitDir))
        {
            return explicitDir;
        }

        var currentDir = Directory.GetCurrentDirectory();
        var solutionRoot = LocateSolutionRoot(currentDir);

        var candidateDirs = new[]
        {
            currentDir,
            solutionRoot,
            solutionRoot is null ? null : Path.Combine(solutionRoot, "src", "Api", "TakeoutSaaS.AdminApi"),
            solutionRoot is null ? null : Path.Combine(solutionRoot, "src", "Api", "TakeoutSaaS.UserApi"),
            solutionRoot is null ? null : Path.Combine(solutionRoot, "src", "Api", "TakeoutSaaS.MiniApi")
        }.Where(dir => !string.IsNullOrWhiteSpace(dir));

        foreach (var dir in candidateDirs)
        {
            if (dir != null && Directory.Exists(dir) && HasAppSettings(dir))
            {
                return dir;
            }
        }

        throw new InvalidOperationException(
            "未找到 appsettings 配置文件，请设置 TAKEOUTSAAS_APPSETTINGS_DIR 环境变量指向包含 appsettings*.json 的目录。");
    }

    private static string? LocateSolutionRoot(string currentPath)
    {
        var directoryInfo = new DirectoryInfo(currentPath);
        while (directoryInfo != null)
        {
            if (File.Exists(Path.Combine(directoryInfo.FullName, "TakeoutSaaS.sln")))
            {
                return directoryInfo.FullName;
            }

            directoryInfo = directoryInfo.Parent;
        }

        return null;
    }

    private static bool HasAppSettings(string directory) =>
        File.Exists(Path.Combine(directory, "appsettings.json")) ||
        Directory.GetFiles(directory, "appsettings.*.json").Length > 0;

    private sealed class DesignTimeTenantProvider : ITenantProvider
    {
        /// <summary>
        /// 设计时返回默认租户 ID。
        /// </summary>
        /// <returns>默认租户 ID。</returns>
        public long GetCurrentTenantId() => 0;
    }

    private sealed class DesignTimeCurrentUserAccessor : ICurrentUserAccessor
    {
        /// <summary>
        /// 设计时用户标识。
        /// </summary>
        public long UserId => 0;
        /// <summary>
        /// 设计时用户鉴权标识。
        /// </summary>
        public bool IsAuthenticated => false;
    }
}
