using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Npgsql;
using TakeoutSaaS.Infrastructure.Identity.Options;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
using DomainIdentityUser = TakeoutSaaS.Domain.Identity.Entities.IdentityUser;
using DomainPermission = TakeoutSaaS.Domain.Identity.Entities.Permission;
using DomainRole = TakeoutSaaS.Domain.Identity.Entities.Role;
using DomainRolePermission = TakeoutSaaS.Domain.Identity.Entities.RolePermission;
using DomainRoleTemplate = TakeoutSaaS.Domain.Identity.Entities.RoleTemplate;
using DomainRoleTemplatePermission = TakeoutSaaS.Domain.Identity.Entities.RoleTemplatePermission;
using DomainUserRole = TakeoutSaaS.Domain.Identity.Entities.UserRole;

namespace TakeoutSaaS.Infrastructure.Identity.Persistence;

/// <summary>
/// 后台账号初始化种子任务
/// </summary>
public sealed class IdentityDataSeeder(IServiceProvider serviceProvider, ILogger<IdentityDataSeeder> logger) : IHostedService
{
    /// <summary>
    /// 执行后台账号与权限种子。
    /// </summary>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>异步任务。</returns>
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        // 1. 创建作用域并解析依赖
        using var scope = serviceProvider.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<IdentityDbContext>();
        var options = scope.ServiceProvider.GetRequiredService<IOptions<AdminSeedOptions>>().Value;
        var passwordHasher = scope.ServiceProvider.GetRequiredService<IPasswordHasher<DomainIdentityUser>>();
        var tenantContextAccessor = scope.ServiceProvider.GetRequiredService<ITenantContextAccessor>();

        // 2. 校验功能开关
        if (!options.Enabled)
        {
            logger.LogInformation("AdminSeed 已禁用，跳过后台账号初始化");
            return;
        }
        // 3. 确保数据库已迁移
        await context.Database.MigrateAsync(cancellationToken);

        // 4. 校验账号配置
        if (options.Users is null or { Count: 0 })
        {
            logger.LogInformation("AdminSeed 未配置账号，跳过后台账号初始化");
            return;
        }

        // 5. 写入角色模板
        await SeedRoleTemplatesAsync(context, options.RoleTemplates, cancellationToken);

        // 6. 逐个账号处理
        foreach (var userOptions in options.Users)
        {
            // 6.1 进入租户作用域
            using var tenantScope = tenantContextAccessor.EnterTenantScope(userOptions.TenantId, "admin-seed");
            // 6.2 查询账号并收集配置
            var user = await context.IdentityUsers.FirstOrDefaultAsync(x => x.Account == userOptions.Account, cancellationToken);
            var roles = NormalizeValues(userOptions.Roles);
            var permissions = NormalizeValues(userOptions.Permissions);

            if (user == null)
            {
                // 6.3 创建新账号
                user = new DomainIdentityUser
                {
                    Id = 0,
                    Account = userOptions.Account,
                    DisplayName = userOptions.DisplayName,
                    TenantId = userOptions.TenantId,
                    MerchantId = userOptions.MerchantId,
                    Avatar = null
                };
                user.PasswordHash = passwordHasher.HashPassword(user, userOptions.Password);
                context.IdentityUsers.Add(user);
                logger.LogInformation("已创建后台账号 {Account}", user.Account);
            }
            else
            {
                // 6.4 更新既有账号
                user.DisplayName = userOptions.DisplayName;
                user.TenantId = userOptions.TenantId;
                user.MerchantId = userOptions.MerchantId;
                user.PasswordHash = passwordHasher.HashPassword(user, userOptions.Password);
                logger.LogInformation("已更新后台账号 {Account}", user.Account);
            }

            // 6.5 确保角色存在
            var existingRoles = await context.Roles
                .Where(r => r.TenantId == userOptions.TenantId && roles.Contains(r.Code))
                .ToListAsync(cancellationToken);
            var existingRoleCodes = existingRoles.Select(r => r.Code).ToHashSet(StringComparer.OrdinalIgnoreCase);
            foreach (var code in roles)
            {
                if (existingRoleCodes.Contains(code))
                {
                    continue;
                }

                context.Roles.Add(new DomainRole
                {
                    TenantId = userOptions.TenantId,
                    Code = code,
                    Name = code,
                    Description = $"Seed role {code}"
                });
            }

            // 6.6 读取当前租户权限定义
            var existingPermissions = await context.Permissions
                .AsNoTracking()
                .Where(p => permissions.Contains(p.Code))
                .ToListAsync(cancellationToken);
            var existingPermissionCodes = existingPermissions
                .Select(p => p.Code)
                .ToHashSet(StringComparer.OrdinalIgnoreCase);
            var missingPermissionCodes = permissions
                .Where(code => !existingPermissionCodes.Contains(code))
                .ToArray();
            if (missingPermissionCodes.Length > 0)
            {
                logger.LogWarning("发现未配置的全局权限编码，已忽略：{Codes}", string.Join(", ", missingPermissionCodes));
            }

            // 6.7 保存基础角色/权限
            await context.SaveChangesAsync(cancellationToken);

            // 6.8 重新加载角色/权限以获取 Id
            var roleEntities = await context.Roles
                .Where(r => r.TenantId == userOptions.TenantId && roles.Contains(r.Code))
                .ToListAsync(cancellationToken);
            var permissionEntities = existingPermissions;

            // 6.9 重置用户角色
            var existingUserRoles = await context.UserRoles
                .Where(ur => ur.TenantId == userOptions.TenantId && ur.UserId == user.Id)
                .ToListAsync(cancellationToken);
            context.UserRoles.RemoveRange(existingUserRoles);
            await context.SaveChangesAsync(cancellationToken);

            var roleIds = roleEntities.Select(r => r.Id).Distinct().ToArray();
            foreach (var roleId in roleIds)
            {
                try
                {
                    var alreadyExists = await context.UserRoles.AnyAsync(
                        ur => ur.TenantId == userOptions.TenantId && ur.UserId == user.Id && ur.RoleId == roleId,
                        cancellationToken);
                    if (alreadyExists)
                    {
                        continue;
                    }

                    await context.UserRoles.AddAsync(new DomainUserRole
                    {
                        TenantId = userOptions.TenantId,
                        UserId = user.Id,
                        RoleId = roleId
                    }, cancellationToken);

                    await context.SaveChangesAsync(cancellationToken);
                }
                catch (DbUpdateException ex) when (ex.InnerException is PostgresException pg && pg.SqlState == PostgresErrorCodes.UniqueViolation)
                {
                    context.ChangeTracker.Clear();
                }
            }

            // 为种子角色绑定种子权限
            if (permissions.Length > 0 && roleIds.Length > 0)
            {
                var permissionIds = permissionEntities.Select(p => p.Id).Distinct().ToArray();
                var existingRolePermissions = await context.RolePermissions
                    .Where(rp => rp.TenantId == userOptions.TenantId && roleIds.Contains(rp.RoleId))
                    .ToListAsync(cancellationToken);
                context.RolePermissions.RemoveRange(existingRolePermissions);
                await context.SaveChangesAsync(cancellationToken);

                var distinctRoleIds = roleIds.Distinct().ToArray();
                var distinctPermissionIds = permissionIds.Distinct().ToArray();
                foreach (var roleId in distinctRoleIds)
                {
                    foreach (var permissionId in distinctPermissionIds)
                    {
                        try
                        {
                            var exists = await context.RolePermissions.AnyAsync(
                                rp => rp.TenantId == userOptions.TenantId
                                   && rp.RoleId == roleId
                                   && rp.PermissionId == permissionId,
                                cancellationToken);
                            if (exists)
                            {
                                continue;
                            }

                            // 6.10 绑定角色与权限
                            await context.RolePermissions.AddAsync(new DomainRolePermission
                            {
                                TenantId = userOptions.TenantId,
                                RoleId = roleId,
                                PermissionId = permissionId
                            }, cancellationToken);

                            await context.SaveChangesAsync(cancellationToken);
                        }
                        catch (DbUpdateException ex) when (ex.InnerException is PostgresException pg && pg.SqlState == PostgresErrorCodes.UniqueViolation)
                        {
                            context.ChangeTracker.Clear();
                        }
                    }
                }
            }
        }

        // 7. 最终保存
        await context.SaveChangesAsync(cancellationToken);
    }

    /// <summary>
    /// 停止生命周期时的清理（此处无需处理）。
    /// </summary>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>已完成任务。</returns>
    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;

    private static async Task SeedRoleTemplatesAsync(
        IdentityDbContext context,
        IList<RoleTemplateSeedOptions> templates,
        CancellationToken cancellationToken)
    {
        // 1. 空集合直接返回
        if (templates is null || templates.Count == 0)
        {
            return;
        }

        // 2. 逐个处理模板
        foreach (var templateOptions in templates)
        {
            // 2.1 校验必填字段
            if (string.IsNullOrWhiteSpace(templateOptions.TemplateCode) || string.IsNullOrWhiteSpace(templateOptions.Name))
            {
                continue;
            }

            // 2.2 查询现有模板
            var code = templateOptions.TemplateCode.Trim();
            var existing = await context.RoleTemplates.FirstOrDefaultAsync(x => x.TemplateCode == code, cancellationToken);

            if (existing == null)
            {
                // 2.3 新增模板
                existing = new DomainRoleTemplate
                {
                    TemplateCode = code,
                    Name = templateOptions.Name.Trim(),
                    Description = templateOptions.Description,
                    IsActive = templateOptions.IsActive
                };

                await context.RoleTemplates.AddAsync(existing, cancellationToken);
                await context.SaveChangesAsync(cancellationToken);
            }
            else
            {
                // 2.4 更新模板
                existing.Name = templateOptions.Name.Trim();
                existing.Description = templateOptions.Description;
                existing.IsActive = templateOptions.IsActive;
                context.RoleTemplates.Update(existing);
                await context.SaveChangesAsync(cancellationToken);
            }

            // 2.5 重置模板权限
            var permissionCodes = NormalizeValues(templateOptions.Permissions);
            var existingPermissions = await context.RoleTemplatePermissions
                .Where(x => x.RoleTemplateId == existing.Id)
                .ToListAsync(cancellationToken);
            // 2.6 清空旧权限并保存
            context.RoleTemplatePermissions.RemoveRange(existingPermissions);
            await context.SaveChangesAsync(cancellationToken);
            // 2.7 去重后的权限编码
            var distinctPermissionCodes = permissionCodes.Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
            foreach (var permissionCode in distinctPermissionCodes)
            {
                try
                {
                    var alreadyExists = await context.RoleTemplatePermissions.AnyAsync(
                        x => x.RoleTemplateId == existing.Id && x.PermissionCode == permissionCode,
                        cancellationToken);
                    if (alreadyExists)
                    {
                        continue;
                    }

                    await context.RoleTemplatePermissions.AddAsync(new DomainRoleTemplatePermission
                    {
                        RoleTemplateId = existing.Id,
                        PermissionCode = permissionCode
                    }, cancellationToken);

                    await context.SaveChangesAsync(cancellationToken);
                }
                catch (DbUpdateException ex) when (ex.InnerException is PostgresException pg && pg.SqlState == PostgresErrorCodes.UniqueViolation)
                {
                    context.ChangeTracker.Clear();
                }
            }
        }
    }

    private static string[] NormalizeValues(string[]? values)
        => values == null
            ? []
            : [.. values
                .Where(v => !string.IsNullOrWhiteSpace(v))
                .Select(v => v.Trim())
                .Distinct(StringComparer.OrdinalIgnoreCase)];
}
