using BCrypt.Net;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using QYZH.InteractiveMagazine.Infrastructure.Auth;
using QYZH.InteractiveMagazine.IService;

using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Models.Settings;
using QYZH.InteractiveMagazine.Repository;

namespace QYZH.InteractiveMagazine.Service;

public class AdminAuthService(
    BaseRepository<AdminUser> adminUserRepository,
    IAdminPermissionService adminPermissionService,
    IConfiguration configuration,
    ILogger<AdminAuthService> logger) : BaseRepository<AdminUser>, IAdminAuthService
{

    private const string TokenKeyPrefix = "InteractiveMagazine:AdminAuth:Token";
    private const string UserInfoKeyPrefix = "InteractiveMagazine:AdminAuth:UserInfo";


    public async Task<AdminLoginOutput> LoginAsync(AdminLoginInput input)
    {
        logger.LogInformation("管理员登录尝试，用户名: {UserName}", input.UserName);

        if (string.IsNullOrWhiteSpace(input.UserName))
        {
            throw new BusinessException("用户名不能为空", ResultCode.BAD_REQUEST);
        }

        if (string.IsNullOrWhiteSpace(input.Password))
        {
            throw new BusinessException("密码不能为空", ResultCode.BAD_REQUEST);
        }

        var adminUser = await adminUserRepository.GetFirstAsync(a => a.UserName == input.UserName);
        if (adminUser == null)
        {
            logger.LogWarning("管理员登录失败，用户名不存在: {UserName}", input.UserName);
            throw new BusinessException("用户名或密码错误", ResultCode.DENY);
        }

        if (!BCrypt.Net.BCrypt.Verify(input.Password, adminUser.PasswordHash))
        {
            logger.LogWarning("管理员登录失败，密码错误: {UserName}", input.UserName);
            throw new BusinessException("用户名或密码错误", ResultCode.DENY);
        }

        if (adminUser.Status != 1)
        {
            logger.LogWarning("管理员登录失败，账号已禁用: {UserName}", input.UserName);
            throw new BusinessException("账号已被禁用，请联系系统管理员", ResultCode.FORBIDDEN);
        }

        var jwtSettings = GetJwtSettings();

        var token = JwtHelper.GenerateToken((long)adminUser.Id, adminUser.UserName, jwtSettings);

        await RedisHelper.SetAsync($"{TokenKeyPrefix}:{adminUser.Id}", token, TimeSpan.FromMinutes(jwtSettings.ExpiryMinutes));

        logger.LogInformation("管理员登录成功，用户名: {UserName}, ID: {UserId}", input.UserName, adminUser.Id);

        var roles = await adminPermissionService.GetAdminUserRolesAsync(adminUser.Id);
        var menus = await adminPermissionService.GetAdminUserMenuTreeAsync(adminUser.Id);
        var permissionCodes = await adminPermissionService.GetAdminUserPermissionCodesAsync(adminUser.Id);

        return new AdminLoginOutput
        {
            Token = token,
            UserId = (long)adminUser.Id,
            UserName = adminUser.UserName,
            Type = adminUser.Type.ToString(),
            RoleIds = roles.Select(x => x.Id).ToList(),
            Roles = roles,
            Menus = menus,
            PermissionCodes = permissionCodes
        };
    }

    public async Task LogoutAsync(long userId)
    {
        logger.LogInformation("管理员登出，ID: {UserId}", userId);

        await RedisHelper.DelAsync($"{TokenKeyPrefix}:{userId}");

        logger.LogInformation("管理员登出成功，ID: {UserId}", userId);
    }

    public async Task<AdminUserInfoOutput> GetAdminInfoAsync(long userId)
    {
        logger.LogInformation("获取管理员信息，ID: {UserId}", userId);

        var adminUser = await adminUserRepository.GetByIdAsync(userId);
        if (adminUser == null)
        {
            logger.LogWarning("未找到管理员，ID: {UserId}", userId);
            throw new BusinessException("用户不存在", ResultCode.NOT_FOUND);
        }

        var roles = await adminPermissionService.GetAdminUserRolesAsync(adminUser.Id);
        var menus = await adminPermissionService.GetAdminUserMenuTreeAsync(adminUser.Id);
        var permissionCodes = await adminPermissionService.GetAdminUserPermissionCodesAsync(adminUser.Id);

        return new AdminUserInfoOutput
        {
            UserId = adminUser.Id,
            UserName = adminUser.UserName,
            Type = adminUser.Type.ToString(),
            Status = adminUser.Status,
            RoleIds = roles.Select(x => x.Id).ToList(),
            Roles = roles,
            Menus = menus,
            PermissionCodes = permissionCodes
        };
    }

    public async Task ChangePasswordAsync(long userId, string oldPassword, string newPassword)
    {
        logger.LogInformation("管理员修改密码尝试，ID: {UserId}", userId);

        if (string.IsNullOrWhiteSpace(oldPassword))
        {
            throw new BusinessException("原密码不能为空", ResultCode.BAD_REQUEST);
        }

        if (string.IsNullOrWhiteSpace(newPassword))
        {
            throw new BusinessException("新密码不能为空", ResultCode.BAD_REQUEST);
        }

        var adminUser = await adminUserRepository.GetByIdAsync(userId);
        if (adminUser == null)
        {
            logger.LogWarning("未找到管理员，ID: {UserId}", userId);
            throw new BusinessException("用户不存在", ResultCode.NOT_FOUND);
        }

        if (!BCrypt.Net.BCrypt.Verify(oldPassword, adminUser.PasswordHash))
        {
            logger.LogWarning("管理员修改密码失败，原密码错误，ID: {UserId}", userId);
            throw new BusinessException("原密码错误", ResultCode.DENY);
        }

        adminUser.PasswordHash = BCrypt.Net.BCrypt.HashPassword(newPassword);

        var result = await adminUserRepository.UpdateAsync(adminUser);
        if (!result)
        {
            throw new BusinessException("修改密码失败", ResultCode.GLOBAL_ERROR);
        }

        await RedisHelper.DelAsync($"{TokenKeyPrefix}:{userId}");

        logger.LogInformation("管理员修改密码成功，ID: {UserId}", userId);
    }

    private JwtSettings GetJwtSettings()
    {
        var jwtSettings = configuration.GetSection("JwtSettings").Get<JwtSettings>()
            ?? new JwtSettings
            {
                Issuer = "QYZH.InteractiveMagazine",
                Audience = "QYZH.InteractiveMagazine",
                SecretKey = "your-256-bit-secret-key-here-change-in-production",
                ExpiryMinutes = 120
            };

        if (string.IsNullOrWhiteSpace(jwtSettings.SecretKey))
        {
            throw new BusinessException("JWT 配置不完整", ResultCode.GLOBAL_ERROR);
        }

        return jwtSettings;
    }
}
