using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using FutureMailAPI.Data;
using FutureMailAPI.DTOs;
using FutureMailAPI.Models;

namespace FutureMailAPI.Services
{
    public class OAuthService : IOAuthService
    {
        private readonly FutureMailDbContext _context;
        private readonly IConfiguration _configuration;
        private readonly ILogger<OAuthService> _logger;

        public OAuthService(FutureMailDbContext context, IConfiguration configuration, ILogger<OAuthService> logger)
        {
            _context = context;
            _configuration = configuration;
            _logger = logger;
        }

        public async Task<ApiResponse<OAuthTokenResponseDto>> LoginAsync(OAuthLoginRequestDto request)
        {
            try
            {
                // 验证OAuth客户端
                var client = await _context.OAuthClients
                    .FirstOrDefaultAsync(c => c.ClientId == request.ClientId && c.ClientSecret == request.ClientSecret);

                if (client == null)
                {
                    return ApiResponse<OAuthTokenResponseDto>.ErrorResult("无效的客户端凭据");
                }

                // 验证用户凭据
                var user = await _context.Users
                    .FirstOrDefaultAsync(u => u.Email == request.Username);

                if (user == null)
                {
                    return ApiResponse<OAuthTokenResponseDto>.ErrorResult("用户不存在");
                }

                // 验证密码
                if (!BCrypt.Net.BCrypt.Verify(request.Password, user.PasswordHash))
                {
                    return ApiResponse<OAuthTokenResponseDto>.ErrorResult("密码错误");
                }

                // 生成访问令牌
                var accessToken = GenerateJwtToken(user, client);
                var refreshToken = GenerateRefreshToken();

                // 保存令牌到数据库
                var oauthToken = new OAuthToken
                {
                    AccessToken = accessToken,
                    RefreshToken = refreshToken,
                    UserId = user.Id,
                    ClientId = client.Id,
                    ExpiresAt = DateTime.UtcNow.AddHours(1), // 访问令牌1小时过期
                    CreatedAt = DateTime.UtcNow
                };

                _context.OAuthTokens.Add(oauthToken);
                await _context.SaveChangesAsync();

                var response = new OAuthTokenResponseDto
                {
                    AccessToken = accessToken,
                    RefreshToken = refreshToken,
                    TokenType = "Bearer",
                    ExpiresIn = 3600 // 1小时，单位秒
                };

                return ApiResponse<OAuthTokenResponseDto>.SuccessResult(response);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "OAuth登录时发生错误");
                return ApiResponse<OAuthTokenResponseDto>.ErrorResult("服务器内部错误");
            }
        }

        public async Task<ApiResponse<OAuthTokenResponseDto>> RefreshTokenAsync(OAuthRefreshTokenRequestDto request)
        {
            try
            {
                // 查找刷新令牌
                var token = await _context.OAuthTokens
                    .Include(t => t.User)
                    .Include(t => t.Client)
                    .FirstOrDefaultAsync(t => t.RefreshToken == request.RefreshToken);

                if (token == null)
                {
                    return ApiResponse<OAuthTokenResponseDto>.ErrorResult("无效的刷新令牌");
                }

                // 生成新的访问令牌
                var accessToken = GenerateJwtToken(token.User, token.Client);
                var refreshToken = GenerateRefreshToken();

                // 更新令牌
                token.AccessToken = accessToken;
                token.RefreshToken = refreshToken;
                token.ExpiresAt = DateTime.UtcNow.AddHours(1);
                token.UpdatedAt = DateTime.UtcNow;

                await _context.SaveChangesAsync();

                var response = new OAuthTokenResponseDto
                {
                    AccessToken = accessToken,
                    RefreshToken = refreshToken,
                    TokenType = "Bearer",
                    ExpiresIn = 3600 // 1小时，单位秒
                };

                return ApiResponse<OAuthTokenResponseDto>.SuccessResult(response);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "OAuth刷新令牌时发生错误");
                return ApiResponse<OAuthTokenResponseDto>.ErrorResult("服务器内部错误");
            }
        }

        public async Task<bool> RevokeTokenAsync(string accessToken)
        {
            try
            {
                var token = await _context.OAuthTokens
                    .FirstOrDefaultAsync(t => t.AccessToken == accessToken);

                if (token != null)
                {
                    _context.OAuthTokens.Remove(token);
                    await _context.SaveChangesAsync();
                    return true;
                }

                return false;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "OAuth撤销令牌时发生错误");
                return false;
            }
        }

        public async Task<OAuthToken?> GetTokenAsync(string accessToken)
        {
            try
            {
                return await _context.OAuthTokens
                    .Include(t => t.User)
                    .Include(t => t.Client)
                    .FirstOrDefaultAsync(t => t.AccessToken == accessToken && t.ExpiresAt > DateTime.UtcNow);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取OAuth令牌时发生错误");
                return null;
            }
        }

        private string GenerateJwtToken(User user, OAuthClient client)
        {
            var jwtSettings = _configuration.GetSection("Jwt");
            var key = Encoding.ASCII.GetBytes(jwtSettings["Key"] ?? throw new InvalidOperationException("JWT密钥未配置"));
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new[]
                {
                    new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                    new Claim(ClaimTypes.Name, user.Username),
                    new Claim(ClaimTypes.Email, user.Email),
                    new Claim("client_id", client.ClientId)
                }),
                Expires = DateTime.UtcNow.AddHours(1),
                Issuer = jwtSettings["Issuer"],
                Audience = jwtSettings["Audience"],
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };

            var tokenHandler = new JwtSecurityTokenHandler();
            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }

        private string GenerateRefreshToken()
        {
            var randomNumber = new byte[32];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }
    }
}