using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using Entities.Contracts;
using Entities.DTO;
using Microsoft.AspNetCore.Identity;
using Moq;
using SharedDATA.Api;
using TechHelper.Features;
using TechHelper.Services.Beta;
using Xunit;

namespace TechHelper.Server.Tests
{
	public class UserServicesTests
	{
		private readonly Mock<IUnitOfWork> _unitOfWorkMock;
		private readonly Mock<IClassService> _classServiceMock;
		private readonly Mock<UserManager<User>> _userManagerMock;
		private readonly Mock<IMapper> _mapperMock;
		private readonly Mock<IEmailSender> _emailSenderMock;
		private readonly UserServices _userServices;

		public UserServicesTests()
		{
			_unitOfWorkMock = new Mock<IUnitOfWork>();
			_classServiceMock = new Mock<IClassService>();
			_userManagerMock = new Mock<UserManager<User>>(
				Mock.Of<IUserStore<User>>(),
				null, null, null, null, null, null, null, null);
			_mapperMock = new Mock<IMapper>();
			_emailSenderMock = new Mock<IEmailSender>();

			_userServices = new UserServices(
				_unitOfWorkMock.Object,
				_classServiceMock.Object,
				_userManagerMock.Object,
				_mapperMock.Object,
				_emailSenderMock.Object);
		}

		[Fact]
		public async Task RegisterNewUserAsync_WhenEmailAlreadyExists_ReturnsErrorMessage()
		{
			// Arrange
			var registrationDto = new UserForRegistrationDto
			{
				Email = "user@example.com",
				Password = "wx1998WX!",
				ConfirmPassword = "wx1998WX!",
				DisplayName = "Test User",
				Roles = UserRoles.Student
			};

			var existingUser = new User { Email = "user@example.com" };
			_userManagerMock.Setup(x => x.FindByEmailAsync(registrationDto.Email))
				.ReturnsAsync(existingUser);

			// Act
			var result = await _userServices.RegisterNewUserAsync(registrationDto);

			// Assert
			Assert.False(result.Status);
			Assert.Contains("此电子邮件地址已被注册", result.Message);
		}

		[Fact]
		public async Task RegisterNewUserAsync_WhenUserCreationFails_ReturnsErrorWithErrors()
		{
			// Arrange
			var registrationDto = new UserForRegistrationDto
			{
				Email = "test@example.com",
				Password = "Password123!",
				ConfirmPassword = "Password123!",
				DisplayName = "Test User",
				Roles = UserRoles.Student
			};

			_userManagerMock.Setup(x => x.FindByEmailAsync(registrationDto.Email))
				.ReturnsAsync((User)null!);

			var user = new User();
			_mapperMock.Setup(x => x.Map<User>(registrationDto))
				.Returns(user);

			_userManagerMock.Setup(x => x.CreateAsync(user, registrationDto.Password))
				.ReturnsAsync(IdentityResult.Failed(new IdentityError { Description = "Password too weak" }));

			// Act
			var result = await _userServices.RegisterNewUserAsync(registrationDto);

			// Assert
			Assert.False(result.Status);
			Assert.NotNull(result.Result);
			Assert.IsType<List<string>>(result.Result);
		}

		[Fact]
		public async Task RegisterNewUserAsync_WhenUserCreatedButNotFound_ReturnsErrorMessage()
		{
			// Arrange
			var registrationDto = new UserForRegistrationDto
			{
				Email = "test@example.com",
				Password = "Password123!",
				ConfirmPassword = "Password123!",
				DisplayName = "Test User",
				Roles = UserRoles.Student
			};

			_userManagerMock.Setup(x => x.FindByEmailAsync(registrationDto.Email))
				.ReturnsAsync((User)null!);

			var user = new User();
			_mapperMock.Setup(x => x.Map<User>(registrationDto))
				.Returns(user);

			var identityResult = IdentityResult.Success;
			_userManagerMock.Setup(x => x.CreateAsync(user, registrationDto.Password))
				.ReturnsAsync(identityResult);

			_userManagerMock.Setup(x => x.FindByEmailAsync(user.Email))
				.ReturnsAsync((User)null!);

			// Act
			var result = await _userServices.RegisterNewUserAsync(registrationDto);

			// Assert
			Assert.False(result.Status);
			Assert.Contains("注册失败，请联系管理员", result.Message);
		}

		[Fact]
		public async Task RegisterNewUserAsync_WhenRoleAssignmentFails_ReturnsError()
		{
			// Arrange
			var registrationDto = new UserForRegistrationDto
			{
				Email = "test@example.com",
				Password = "Password123!",
				ConfirmPassword = "Password123!",
				DisplayName = "Test User",
				Roles = UserRoles.Student
			};

			_userManagerMock.Setup(x => x.FindByEmailAsync(registrationDto.Email))
				.ReturnsAsync((User)null!);

			var user = new User();
			_mapperMock.Setup(x => x.Map<User>(registrationDto))
				.Returns(user);

			var identityResult = IdentityResult.Success;
			_userManagerMock.Setup(x => x.CreateAsync(user, registrationDto.Password))
				.ReturnsAsync(identityResult);

			var createdUser = new User { Id = Guid.NewGuid(), Email = registrationDto.Email };
			_userManagerMock.Setup(x => x.FindByEmailAsync(user.Email))
				.ReturnsAsync(createdUser);

			_userManagerMock.Setup(x => x.AddToRoleAsync(createdUser, registrationDto.Roles.ToString()))
				.ReturnsAsync(IdentityResult.Failed(new IdentityError { Description = "Role assignment failed" }));

			// Act
			var result = await _userServices.RegisterNewUserAsync(registrationDto);

			// Assert
			Assert.False(result.Status);
			Assert.NotNull(result.Result);
			Assert.IsType<List<string>>(result.Result);
		}

		[Fact]
		public async Task RegisterNewUserAsync_WhenClassRegistrationFails_DeletesUserAndReturnsError()
		{
			// Arrange
			var registrationDto = new UserForRegistrationDto
			{
				Email = "test@example.com",
				Password = "Password123!",
				ConfirmPassword = "Password123!",
				DisplayName = "Test User",
				Roles = UserRoles.Student,
				RegisterUserToClassDto = new RegisterUserToClassDto
				{
					UserId = Guid.NewGuid(),
					ClassId = Guid.NewGuid()
				}
			};

			_userManagerMock.Setup(x => x.FindByEmailAsync(registrationDto.Email))
				.ReturnsAsync((User)null!);

			var user = new User();
			_mapperMock.Setup(x => x.Map<User>(registrationDto))
				.Returns(user);

			var identityResult = IdentityResult.Success;
			_userManagerMock.Setup(x => x.CreateAsync(user, registrationDto.Password))
				.ReturnsAsync(identityResult);

			var createdUser = new User { Id = Guid.NewGuid(), Email = registrationDto.Email };
			_userManagerMock.Setup(x => x.FindByEmailAsync(user.Email))
				.ReturnsAsync(createdUser);

			_userManagerMock.Setup(x => x.AddToRoleAsync(createdUser, registrationDto.Roles.ToString()))
				.ReturnsAsync(IdentityResult.Success);

			_classServiceMock.Setup(x => x.UserRegister(registrationDto.RegisterUserToClassDto))
				.ReturnsAsync(new ApiResponse(false, "Class registration failed"));

			// Act
			var result = await _userServices.RegisterNewUserAsync(registrationDto);

			// Assert
			Assert.False(result.Status);
			Assert.Contains("Class registration failed", result.Message);

			// Verify that the user was deleted
			_userManagerMock.Verify(x => x.DeleteAsync(createdUser), Times.Once);
		}

		[Fact]
		public async Task RegisterNewUserAsync_WhenAllStepsSucceed_ReturnsSuccess()
		{
			// Arrange
			var registrationDto = new UserForRegistrationDto
			{
				Email = "test@example.com",
				Password = "Password123!",
				ConfirmPassword = "Password123!",
				DisplayName = "Test User",
				Roles = UserRoles.Student,
				RegisterUserToClassDto = new RegisterUserToClassDto
				{
					UserId = Guid.NewGuid(),
					ClassId = Guid.NewGuid()
				}
			};

			_userManagerMock.Setup(x => x.FindByEmailAsync(registrationDto.Email))
				.ReturnsAsync((User)null!);

			var user = new User();
			_mapperMock.Setup(x => x.Map<User>(registrationDto))
				.Returns(user);

			var identityResult = IdentityResult.Success;
			_userManagerMock.Setup(x => x.CreateAsync(user, registrationDto.Password))
				.ReturnsAsync(identityResult);

			var createdUser = new User { Id = Guid.NewGuid(), Email = registrationDto.Email };
			_userManagerMock.Setup(x => x.FindByEmailAsync(user.Email))
				.ReturnsAsync(createdUser);

			_userManagerMock.Setup(x => x.AddToRoleAsync(createdUser, registrationDto.Roles.ToString()))
				.ReturnsAsync(IdentityResult.Success);

			_classServiceMock.Setup(x => x.UserRegister(registrationDto.RegisterUserToClassDto))
				.ReturnsAsync(new ApiResponse(true, "操作成功。"));

			// Act
			var result = await _userServices.RegisterNewUserAsync(registrationDto);

			// Assert
			Assert.True(result.Status);
			Assert.Equal("操作成功。", result.Message);
		}

		[Fact]
		public async Task RegisterNewUserAsync_WhenExceptionOccurs_DeletesUserAndReturnsError()
		{
			// Arrange
			var registrationDto = new UserForRegistrationDto
			{
				Email = "test@example.com",
				Password = "Password123!",
				ConfirmPassword = "Password123!",
				DisplayName = "Test User",
				Roles = UserRoles.Student
			};

			_userManagerMock.Setup(x => x.FindByEmailAsync(registrationDto.Email))
				.ReturnsAsync((User)null!);

			var user = new User();
			_mapperMock.Setup(x => x.Map<User>(registrationDto))
				.Returns(user);

			_userManagerMock.Setup(x => x.CreateAsync(user, registrationDto.Password))
				.ThrowsAsync(new Exception("Database error"));

			var createdUser = new User { Id = Guid.NewGuid(), Email = registrationDto.Email };
			_userManagerMock.Setup(x => x.FindByEmailAsync(registrationDto.Email))
				.ReturnsAsync(createdUser);

			// Act
			var result = await _userServices.RegisterNewUserAsync(registrationDto);

			// Assert
			Assert.False(result.Status);
			Assert.Contains("注册过程中发生错误", result.Message);

			// Verify that the user was deleted
			_userManagerMock.Verify(x => x.DeleteAsync(createdUser), Times.Once);
		}

		[Fact]
		public async Task RegisterNewUserAsync_SetsCorrectUserProperties()
		{
			// Arrange
			var registrationDto = new UserForRegistrationDto
			{
				Email = "test@example.com",
				Password = "Password123!",
				ConfirmPassword = "Password123!",
				DisplayName = "Test User",
				Roles = UserRoles.Teacher
			};

			_userManagerMock.Setup(x => x.FindByEmailAsync(registrationDto.Email))
				.ReturnsAsync((User)null!);

			var mappedUser = new User();
			_mapperMock.Setup(x => x.Map<User>(registrationDto))
				.Returns(mappedUser);

			// Act
			await _userServices.RegisterNewUserAsync(registrationDto);

			// Assert
			Assert.Equal(registrationDto.Email, mappedUser.UserName);
			Assert.Equal(registrationDto.DisplayName, mappedUser.DisplayName);
			Assert.Equal(registrationDto.Roles, mappedUser.Role);
			Assert.False(mappedUser.EmailConfirmed);
		}
	}
}
