using Entities.DTO;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using TechHelper.Features;
using Microsoft.AspNetCore.WebUtilities;
using Entities.Contracts;
using TechHelper.Services.Beta;

namespace TechHelper.Controllers
{
	/// <summary>
	/// 账户管理控制器
	/// 处理用户注册、登录、密码重置等认证相关操作
	/// </summary>
	[Route("api/account")]
	[ApiController]
	public class AccountController : ControllerBase
	{
		private readonly UserManager<User> _userManager;
		private IAuthenticationService _authenticationService;
		private readonly IEmailSender _emailSender;
		private readonly IUserSerivces _userSerivces;

		/// <summary>
		/// 初始化账户控制器
		/// </summary>
		/// <param name="userManager">用户管理服务</param>
		/// <param name="userRegistrationService">用户注册服务</param>
		/// <param name="emailSender">邮件发送服务</param>
		/// <param name="authenticationService">认证服务</param>
		public AccountController(UserManager<User> userManager,
			IEmailSender emailSender,
			IUserSerivces userSerivces,
			IAuthenticationService authenticationService)
		{
			_userManager = userManager;
			_emailSender = emailSender;
			_userSerivces = userSerivces;
			_authenticationService = authenticationService;
		}

		/// <summary>
		/// 注册新用户
		/// </summary>
		/// <param name="userForRegistrationDto">用户注册信息数据传输对象</param>
		/// <returns>注册结果响应</returns>
		/// <response code="201">用户注册成功</response>
		/// <response code="400">注册请求无效或验证失败</response>
		[HttpPost("register")]
		public async Task<IActionResult> RegisterUsesr(
			[FromBody] UserForRegistrationDto userForRegistrationDto)
		{
			#region V1
			//if (userForRegistrationDto == null || !ModelState.IsValid)
			//	return BadRequest();

			//var user = new User
			//{
			//	UserName = userForRegistrationDto.Name,
			//	Email = userForRegistrationDto.Email,
			//	PhoneNumber = userForRegistrationDto.PhoneNumber,
			//	Address = userForRegistrationDto.HomeAddress
			//};

			//var result = await _userManager.CreateAsync(user, userForRegistrationDto.Password);
			//if (!result.Succeeded)
			//{
			//	var errors = result.Errors.Select(e => e.Description);
			//	return BadRequest(new ResponseDto { Errors = errors });
			//}

			//await _userManager.SetTwoFactorEnabledAsync(user, true); 
			//var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);

			//var param = new Dictionary<string, string>
			//{
			//	{"token", token},
			//	{"email", userForRegistrationDto.Email}
			//};

			//var callback = QueryHelpers.AddQueryString(userForRegistrationDto.ClientURI, param);
			//try
			//{
			//	await _emailSender.SendEmailAsync(user.Email.ToString(), "Email Confirmation token", callback);
			//}
			//catch (Exception ex)
			//{
			//}

			//await _userManager.AddToRoleAsync(user, userForRegistrationDto.Roles.ToString());

			//return StatusCode(201);
			#endregion
			#region V2

			if (!ModelState.IsValid)
				return BadRequest(new ApiResponse(false, ModelState));

			var response = await _userSerivces.RegisterNewUserAsync(userForRegistrationDto);

			if (response.Status)
			{
				return StatusCode(201, response);
			}
			else
			{
				return BadRequest(response);
			}
			#endregion
		}

		/// <summary>
		/// 用户登录认证
		/// </summary>
		/// <param name="userForAuthentication">用户认证信息数据传输对象</param>
		/// <returns>认证结果响应</returns>
		/// <response code="200">登录成功，返回认证令牌</response>
		/// <response code="401">认证失败，用户名或密码错误</response>
		/// <response code="400">请求无效或验证失败</response>
		[HttpPost("login")]
		public async Task<IActionResult> Logion(
			[FromBody] UserForAuthenticationDto userForAuthentication)
		{
			var user = await _userManager.FindByEmailAsync(userForAuthentication.Email);

			if (user == null )
			{
				return Unauthorized(new AuthResponseDto
				{
					ErrorMessage = "Invalid Request"
				});
			}

			//if(!await _userManager.IsEmailConfirmedAsync(user))
			//	return Unauthorized(new AuthResponseDto
			//	{
			//		ErrorMessage = "Email is not confirmed"
			//	});

			if(await _userManager.IsLockedOutAsync(user))
				return Unauthorized(new AuthResponseDto
				{
					ErrorMessage = "This account is locked out"
				});

			if (!await _userManager.CheckPasswordAsync(user,
				userForAuthentication.Password))
			{
				await _userManager.AccessFailedAsync(user);

				if(await _userManager.IsLockedOutAsync(user))
				{
					var content = $"Your account is locked out."
						+ $"if you want to reset the password, you can use Forgot password link On the login page";

					await _emailSender.SendEmailAsync(user.Email, "Locked out account information" ,content );
				}

				return Unauthorized(new AuthResponseDto
				{
					ErrorMessage = "Invalid Authentication"
				});
			}

			if(await _userManager.GetTwoFactorEnabledAsync(user))
				return await GenerateOTPFor2StepVerification(user);

			var token = await _authenticationService.GetToken(user);

			user.RefreshToken = _authenticationService.GenerateRefreshToken();
			user.RefreshTokenExpiryTime = DateTime.Now.AddDays(7);
			await _userManager.UpdateAsync(user);


			await _userManager.ResetAccessFailedCountAsync(user);

			return Ok(new AuthResponseDto
			{
				IsAuthSuccessful = true,
				Token = token,
				RefreshToken = user.RefreshToken
			});
		}

		[HttpPost("initadmin")]
		public async Task<IActionResult> InitAdmin([FromBody] UserForAdmin admin)
		{
			var result = await _userSerivces.InitAdminUser(admin);
			if(result.Status)
			{
				return Ok(result.Message);
			}
			return BadRequest(result.Message);
		}

		/// <summary>
		/// 生成两步验证的OTP令牌
		/// </summary>
		/// <param name="user">用户对象</param>
		/// <returns>两步验证响应</returns>
		private async Task<IActionResult> GenerateOTPFor2StepVerification(User user)
		{
			var providers = await _userManager.GetValidTwoFactorProvidersAsync(user);
			if(!providers.Contains("Email"))
			{
				return Unauthorized(new AuthResponseDto
				{
					ErrorMessage = "Invalid 2-Step verification Provider"
				});
			}

			var token = await _userManager.GenerateTwoFactorTokenAsync(user, "Email");

			await _emailSender.SendEmailAsync(user.Email, "Authentication token", token);

			return Ok(new AuthResponseDto
			{
				Is2StepVerificationRequired = true,
				Provider = "Email"
			});
		}

		/// <summary>
		/// 忘记密码请求
		/// 发送密码重置令牌到用户邮箱
		/// </summary>
		/// <param name="forgotPasswordDto">忘记密码请求数据传输对象</param>
		/// <returns>操作结果</returns>
		/// <response code="200">密码重置邮件发送成功</response>
		/// <response code="400">请求无效或用户不存在</response>
		[HttpPost("forgotPassword")]
		public async Task<IActionResult> ForgotPassword(
			[FromBody] ForgotPasswordDto forgotPasswordDto)
		{
			var user = await _userManager.FindByEmailAsync(forgotPasswordDto.Email);
			if (user == null)
				return BadRequest("Invalid Request");

			var token = await _userManager.GeneratePasswordResetTokenAsync(user);

			var param = new Dictionary<string, string>
			{
				{"token", token},
				{"email", forgotPasswordDto.Email}
			};

			var callback = QueryHelpers.AddQueryString(forgotPasswordDto.ClientURI, param);
			await _emailSender.SendEmailAsync( user.Email.ToString() , "Reset Password Token", callback);

			return Ok();
		}


		/// <summary>
		/// 重置用户密码
		/// </summary>
		/// <param name="resetPasswordDto">密码重置数据传输对象</param>
		/// <returns>重置结果响应</returns>
		/// <response code="200">密码重置成功</response>
		/// <response code="400">密码重置失败</response>
		[HttpPost("resetPassword")]
		public async Task<IActionResult> ResetPassword(
			[FromBody] ResetPasswordDto resetPasswordDto)
		{
			var errorResponse = new ResetPasswordResponseDto
			{
				Errors = new string[] { "Reset Password Failed" }
			};
			if (!ModelState.IsValid)
				return BadRequest(errorResponse);

			var user = await _userManager.FindByEmailAsync(resetPasswordDto.Email);
			if (user == null) return BadRequest(errorResponse);

			var resetPassResult = await _userManager.ResetPasswordAsync(user,
				resetPasswordDto.Token, resetPasswordDto.Password);

			if (!resetPassResult.Succeeded)
			{
				var errors = resetPassResult.Errors.Select(e => e.Description);
				return BadRequest(new ResetPasswordResponseDto { Errors = errors });
			}

			await _userManager.SetLockoutEndDateAsync(user, null);

			return Ok(new ResetPasswordResponseDto { IsResetPasswordSuccessful = true});
		}

		/// <summary>
		/// 邮箱确认验证
		/// 验证用户邮箱确认令牌
		/// </summary>
		/// <param name="email">用户邮箱地址</param>
		/// <param name="token">邮箱确认令牌</param>
		/// <returns>验证结果</returns>
		/// <response code="200">邮箱确认成功</response>
		/// <response code="400">邮箱确认失败</response>
		[HttpGet("emailconfirmation")]
		public async Task<IActionResult> EmailConfirmaation([FromQuery] string email,
			[FromQuery] string token)
		{
			var user = await _userManager.FindByEmailAsync(email);
			if (user == null) return BadRequest();

			var confimResult = await _userManager.ConfirmEmailAsync(user, token);
			if (!confimResult.Succeeded) return BadRequest();

			return Ok();
		}


		/// <summary>
		/// 两步验证确认
		/// 验证用户提供的两步验证令牌
		/// </summary>
		/// <param name="twoFactorVerificationDto">两步验证数据传输对象</param>
		/// <returns>验证结果响应</returns>
		/// <response code="200">验证成功，返回认证令牌</response>
		/// <response code="400">验证失败</response>
		[HttpPost("TwoStepVerification")]
		public async Task<IActionResult> TwoStepVerification(
			[FromBody] TwoFactorVerificationDto twoFactorVerificationDto)
		{
			if(!ModelState.IsValid) return BadRequest(
				new AuthResponseDto
				{
					ErrorMessage = "Invalid Request"
				});


			var user = await _userManager.FindByEmailAsync(twoFactorVerificationDto.Email);
			if(user == null) return BadRequest(
				new AuthResponseDto
				{
					ErrorMessage = "Invalid Request"
				});

			var validVerification = await _userManager.VerifyTwoFactorTokenAsync(user, twoFactorVerificationDto.Provider, twoFactorVerificationDto.TwoFactorToken);
			if(!validVerification) return BadRequest(
				new AuthResponseDto
				{
					ErrorMessage = "Invalid Request"
				});

			var token = await _authenticationService.GetToken(user);
			user.RefreshToken = _authenticationService.GenerateRefreshToken();
			user.RefreshTokenExpiryTime = DateTime.Now.AddDays(7);
			await _userManager.UpdateAsync(user);
			await _userManager.ResetAccessFailedCountAsync(user);

			return Ok(new AuthResponseDto
			{
				IsAuthSuccessful = true,
				Token = token,
				RefreshToken = user.RefreshToken
			});
		}
	}
}
