using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Hua.Todo.Application.CloudSync.Auth;
using Hua.Todo.Application.CloudSync.Models;
using Hua.Todo.Application.Data;
using Hua.Todo.Core.Entities;

namespace Hua.Todo.Application.CloudSync.Services;

/// <summary>
/// 云同步认证服务（登录、初始化管理员、二次认证）。
/// </summary>
public class CloudAuthService
{
    private readonly TodoDbContext _dbContext;
    private readonly IPasswordHasher<UserEntity> _passwordHasher;
    private readonly IRolePermissionMapper _rolePermissionMapper;

    /// <summary>
    /// 创建 <see cref="CloudAuthService"/>。
    /// </summary>
    /// <param name="dbContext">数据库上下文。</param>
    /// <param name="passwordHasher">密码哈希器。</param>
    /// <param name="rolePermissionMapper">角色权限映射器。</param>
    public CloudAuthService(
        TodoDbContext dbContext,
        IPasswordHasher<UserEntity> passwordHasher,
        IRolePermissionMapper rolePermissionMapper)
    {
        _dbContext = dbContext;
        _passwordHasher = passwordHasher;
        _rolePermissionMapper = rolePermissionMapper;
    }

    /// <summary>
    /// 初始化系统管理员账号（仅在系统尚无云用户时可用）。
    /// </summary>
    /// <param name="userName">用户名。</param>
    /// <param name="password">密码。</param>
    /// <param name="cancellationToken">取消令牌。</param>
    /// <returns>是否初始化成功。</returns>
    public async Task<bool> BootstrapAdminAsync(string userName, string password, CancellationToken cancellationToken)
    {
        var normalizedUserName = (userName ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(normalizedUserName) || string.IsNullOrWhiteSpace(password))
        {
            return false;
        }

        var hasAnyCloudUser = await _dbContext.Users
            .AsNoTracking()
            .AnyAsync(u => u.Role != "local", cancellationToken);

        if (hasAnyCloudUser)
        {
            return false;
        }

        var exists = await _dbContext.Users
            .AsNoTracking()
            .AnyAsync(u => u.UserName == normalizedUserName, cancellationToken);

        if (exists)
        {
            return false;
        }

        var user = new UserEntity
        {
            Id = Guid.NewGuid(),
            UserName = normalizedUserName,
            Role = "admin"
        };

        user.PasswordHash = _passwordHasher.HashPassword(user, password);

        _dbContext.Users.Add(user);
        _dbContext.SecurityPolicies.Add(new SecurityPolicyEntity { Id = Guid.NewGuid(), UserId = user.Id, AllowPersist = true });
        await _dbContext.SaveChangesAsync(cancellationToken);

        return true;
    }

    /// <summary>
    /// 用户名密码登录并创建会话。
    /// </summary>
    /// <param name="userName">用户名。</param>
    /// <param name="password">密码。</param>
    /// <param name="sessionTtl">会话有效期。</param>
    /// <param name="cancellationToken">取消令牌。</param>
    /// <returns>登录响应；失败则返回 null。</returns>
    public async Task<LoginResponse?> LoginAsync(string userName, string password, TimeSpan sessionTtl, CancellationToken cancellationToken)
    {
        var normalizedUserName = (userName ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(normalizedUserName) || string.IsNullOrWhiteSpace(password))
        {
            return null;
        }

        var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.UserName == normalizedUserName, cancellationToken);
        if (user == null || user.Role == "local")
        {
            return null;
        }

        var verify = _passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password);
        if (verify == PasswordVerificationResult.Failed)
        {
            return null;
        }

        var now = DateTime.UtcNow;
        var expiresAt = now.Add(sessionTtl);

        var session = new UserSessionEntity
        {
            Id = Guid.NewGuid(),
            UserId = user.Id,
            CreatedAtUtc = now,
            ExpiresAtUtc = expiresAt
        };

        _dbContext.UserSessions.Add(session);

        var hasPolicy = await _dbContext.SecurityPolicies
            .AsNoTracking()
            .AnyAsync(p => p.UserId == user.Id, cancellationToken);

        if (!hasPolicy)
        {
            _dbContext.SecurityPolicies.Add(new SecurityPolicyEntity { Id = Guid.NewGuid(), UserId = user.Id, AllowPersist = true });
        }

        await _dbContext.SaveChangesAsync(cancellationToken);

        return new LoginResponse
        {
            AccessToken = session.Id.ToString(),
            ExpiresAtUtc = expiresAt,
            UserId = user.Id,
            Role = user.Role,
            Permissions = _rolePermissionMapper.GetPermissions(user.Role).ToList()
        };
    }

    /// <summary>
    /// 通过再输入口令提升会话权限（step-up）。
    /// </summary>
    /// <param name="sessionId">会话 ID。</param>
    /// <param name="password">口令。</param>
    /// <param name="stepUpTtl">二次认证有效期。</param>
    /// <param name="cancellationToken">取消令牌。</param>
    /// <returns>有效期截止时间；失败返回 null。</returns>
    public async Task<DateTime?> StepUpAsync(Guid sessionId, string password, TimeSpan stepUpTtl, CancellationToken cancellationToken)
    {
        if (string.IsNullOrWhiteSpace(password))
        {
            return null;
        }

        var now = DateTime.UtcNow;

        var session = await _dbContext.UserSessions.FirstOrDefaultAsync(s => s.Id == sessionId, cancellationToken);
        if (session == null || session.ExpiresAtUtc <= now)
        {
            return null;
        }

        var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == session.UserId, cancellationToken);
        if (user == null)
        {
            return null;
        }

        var verify = _passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password);
        if (verify == PasswordVerificationResult.Failed)
        {
            return null;
        }

        var stepUpExpiresAt = now.Add(stepUpTtl);
        session.StepUpExpiresAtUtc = stepUpExpiresAt;
        await _dbContext.SaveChangesAsync(cancellationToken);

        return stepUpExpiresAt;
    }
}

