using FreeSql;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using BookmarkApi.Data.Entities;

namespace BookmarkApi.Data;

/// <summary>
/// FreeSql 配置扩展
/// </summary>
public static class FreeSqlSetup
{
    /// <summary>
    /// 所有实体类型（用于数据库初始化）
    /// </summary>
    private static readonly Type[] EntityTypes = new[]
    {
        // 核心表
        typeof(User),
        typeof(Device),
        typeof(RefreshToken),
        // 书签与组织
        typeof(Folder),
        typeof(Bookmark),
        typeof(Tag),
        typeof(BookmarkTag),
        // 扩展功能
        typeof(BookmarkDevicePermission),
        typeof(Collection),
        typeof(CollectionBookmark),
        typeof(BookmarkShare),
        typeof(BookmarkVisit)
    };

    /// <summary>
    /// 添加 FreeSql 服务
    /// </summary>
    public static IServiceCollection AddFreeSql(this IServiceCollection services, IConfiguration configuration)
    {
        var connectionString = configuration.GetConnectionString("DefaultConnection");
        var provider = configuration["DatabaseProvider"]?.ToLower() ?? "sqlserver";

        var dataType = provider switch
        {
            "postgresql" or "pgsql" => DataType.PostgreSQL,
            "sqlserver" or "mssql" => DataType.SqlServer,
            _ => DataType.SqlServer
        };

        var freeSql = new FreeSqlBuilder()
            .UseConnectionString(dataType, connectionString)
            .UseAutoSyncStructure(true) // 自动同步实体结构到数据库
            .UseMonitorCommand(cmd => Console.WriteLine($"[FreeSql] {cmd.CommandText}")) // 开发时打印 SQL
            .Build();

        // 注册为单例
        services.AddSingleton<IFreeSql>(freeSql);

        return services;
    }

    /// <summary>
    /// 初始化数据库（创建表、种子数据等）
    /// </summary>
    public static async Task InitializeDatabaseAsync(IFreeSql freeSql)
    {
        Console.WriteLine("[FreeSql] Starting database initialization...");

        // 同步所有实体表结构
        SyncAllTables(freeSql);

        // 创建超级管理员账号（如果不存在）
        await SeedSuperAdminAsync(freeSql);

        Console.WriteLine("[FreeSql] Database initialization completed.");
    }

    /// <summary>
    /// 同步所有表结构
    /// </summary>
    public static void SyncAllTables(IFreeSql freeSql)
    {
        Console.WriteLine($"[FreeSql] Syncing {EntityTypes.Length} tables...");

        foreach (var entityType in EntityTypes)
        {
            try
            {
                freeSql.CodeFirst.SyncStructure(entityType);
                Console.WriteLine($"[FreeSql] Synced table for {entityType.Name}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[FreeSql] Error syncing table for {entityType.Name}: {ex.Message}");
                throw;
            }
        }
    }

    /// <summary>
    /// 检查数据库连接和表是否存在
    /// </summary>
    public static async Task<DatabaseStatus> CheckDatabaseStatusAsync(IFreeSql freeSql)
    {
        var status = new DatabaseStatus();

        try
        {
            // 测试数据库连接
            await freeSql.Ado.ExecuteScalarAsync("SELECT 1");
            status.IsConnected = true;

            // 检查各表是否存在
            foreach (var entityType in EntityTypes)
            {
                var tableName = GetTableName(entityType);
                var exists = await CheckTableExistsAsync(freeSql, tableName);
                status.Tables[entityType.Name] = exists;
            }
        }
        catch (Exception ex)
        {
            status.IsConnected = false;
            status.ErrorMessage = ex.Message;
        }

        return status;
    }

    /// <summary>
    /// 获取实体对应的表名
    /// </summary>
    private static string GetTableName(Type entityType)
    {
        var tableAttr = entityType.GetCustomAttributes(typeof(FreeSql.DataAnnotations.TableAttribute), false)
            .FirstOrDefault() as FreeSql.DataAnnotations.TableAttribute;
        return tableAttr?.Name ?? entityType.Name.ToLower() + "s";
    }

    /// <summary>
    /// 检查表是否存在
    /// </summary>
    private static async Task<bool> CheckTableExistsAsync(IFreeSql freeSql, string tableName)
    {
        try
        {
            var dbType = freeSql.Ado.DataType;
            string sql = dbType switch
            {
                DataType.PostgreSQL => $"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = '{tableName}')",
                DataType.SqlServer => $"SELECT CASE WHEN EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '{tableName}') THEN 1 ELSE 0 END",
                _ => throw new NotSupportedException($"Database type {dbType} is not supported for table existence check")
            };

            var result = await freeSql.Ado.ExecuteScalarAsync(sql);
            return Convert.ToBoolean(result);
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// 创建超级管理员种子数据
    /// </summary>
    private static async Task SeedSuperAdminAsync(IFreeSql freeSql)
    {
        const string adminEmail = "admin@bookmarks.local";

        var adminExists = await freeSql.Select<User>()
            .Where(u => u.Email == adminEmail)
            .AnyAsync();

        if (!adminExists)
        {
            var admin = new User
            {
                Id = Guid.NewGuid(),
                Email = adminEmail,
                UserName = "Administrator",
                PasswordHash = BCrypt.Net.BCrypt.HashPassword("Admin@123"),
                Role = Shared.Enums.AdminRole.SuperAdmin,
                Status = Shared.Enums.UserStatus.Normal,
                CreatedAt = DateTime.UtcNow
            };

            await freeSql.Insert(admin).ExecuteAffrowsAsync();
            Console.WriteLine($"[FreeSql] Created super admin account: {adminEmail}");
        }
    }
}

/// <summary>
/// 数据库状态信息
/// </summary>
public class DatabaseStatus
{
    /// <summary>
    /// 是否已连接
    /// </summary>
    public bool IsConnected { get; set; }

    /// <summary>
    /// 错误信息
    /// </summary>
    public string? ErrorMessage { get; set; }

    /// <summary>
    /// 各表是否存在
    /// </summary>
    public Dictionary<string, bool> Tables { get; set; } = new();

    /// <summary>
    /// 是否所有表都存在
    /// </summary>
    public bool AllTablesExist => IsConnected && Tables.Count > 0 && Tables.Values.All(v => v);

    /// <summary>
    /// 缺失的表
    /// </summary>
    public IEnumerable<string> MissingTables => Tables.Where(t => !t.Value).Select(t => t.Key);
}

