namespace DocuMed.Core.EntityServices;


public class UserService : IUserService
{
    private readonly ICurrentUserService _currentUserService;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<ApplicationRole> _roleManager;
    private readonly IRepositoryWrapper _repositoryWrapper;

    public UserService(ICurrentUserService currentUserService,
        UserManager<ApplicationUser> userManager,
        RoleManager<ApplicationRole> roleManager,
        IRepositoryWrapper repositoryWrapper)
    {
        _currentUserService = currentUserService;
        _userManager = userManager;
        _roleManager = roleManager;
        _repositoryWrapper = repositoryWrapper;
    }

    public async Task<List<ApplicationUserSDto>> GetUsersAsync(int page = 0, CancellationToken cancellationToken = default)
    {
        var users = await _userManager.Users.Select(ApplicationUserMapper.ProjectToSDto).ToListAsync(cancellationToken);

        return users;
    }

    public async Task<ApplicationUserSDto> GetUserAsync(Guid userId)
        => (await _userManager.FindByIdAsync(userId.ToString())).AdaptToSDto();

    public async Task<ApplicationUser> CreateUserAsync(string phoneNumber)
    {
        var user = new ApplicationUser
        {
            UserName = phoneNumber,
            PhoneNumber = phoneNumber,
            SignUpStatus = SignUpStatus.StartSignUp
        };
        var result = await _userManager.CreateAsync(user);
        if (!result.Succeeded)
            throw new AppException(string.Join('|', result.Errors));
        return user;
    }

    public async Task<ApplicationUser> CreateUserAsync(UserActionRequestDto request, CancellationToken cancellationToken)
    {
        var user = new ApplicationUser
        {
            UserName = request.PhoneNumber,
            PhoneNumber = request.PhoneNumber,
            FirstName = request.FirstName,
            LastName = request.LastName,
            NationalId = request.NationalId,
            BirthDate = request.BirthDate,
            Gender = request.Gender,
            SignUpStatus = SignUpStatus.SignUpCompleted,
            UniversityId = request.UniversityId
        };
        if (!request.Password.IsNullOrEmpty())
        {
            var result = await _userManager.CreateAsync(user, request.Password);
            if (!result.Succeeded)
                throw new AppException(string.Join('|', result.Errors));
        }
        else
        {
            var result = await _userManager.CreateAsync(user);
            if (!result.Succeeded)
                throw new AppException(string.Join('|', result.Errors));
        }

        var roleResult = await _userManager.AddToRoleAsync(user, RoleNames.Student);
        if (!roleResult.Succeeded)
            throw new AppException(string.Join('|', roleResult.Errors));

        return user;
    }

    public async Task<bool> EditUserAsync(UserActionRequestDto request, CancellationToken cancellationToken)
    {
        if (_currentUserService.UserId == null)
            throw new AppException("Wrong authorize token , UserId needed");

        var user = await _userManager.FindByIdAsync(_currentUserService.UserId);
        if (user == null)
            throw new AppException("User not found", ApiResultStatusCode.NotFound);
        user.LastName = request.LastName;
        user.FirstName = request.FirstName;
        user.UserName = request.PhoneNumber;
        user.PhoneNumber = request.PhoneNumber;
        user.StudentId = request.StudentId;
        user.FirstName = request.FirstName;
        user.LastName = request.LastName;
        user.NationalId = request.NationalId;
        user.BirthDate = request.BirthDate;
        user.Gender = request.Gender;
        if (request.UniversityId != Guid.Empty)
            user.UniversityId = request.UniversityId;
        if (request.SectionId != Guid.Empty)
            user.SectionId = request.SectionId;

        var result = await _userManager.UpdateAsync(user);
        if (!result.Succeeded)
            throw new AppException(string.Join('|', result.Errors));
        if (!request.Password.IsNullOrEmpty())
        {
            if (await _userManager.HasPasswordAsync(user))
                await _userManager.RemovePasswordAsync(user);

            var addPassResult = await _userManager.AddPasswordAsync(user, request.Password);
            if (!addPassResult.Succeeded)
                throw new AppException(string.Join('|', addPassResult.Errors));
        }
        return true;
    }

    public async Task<bool> RemoveUserAsync(Guid userId, CancellationToken cancellationToken)
    {
        var user = await _userManager.FindByIdAsync(userId.ToString());
        if (user == null)
            throw new AppException("User not found", ApiResultStatusCode.NotFound);
        var removeResult = await _userManager.DeleteAsync(user);
        if (!removeResult.Succeeded)
            throw new AppException(string.Join('|', removeResult.Errors));
        return true;
    }



    public async Task<List<ApplicationRole>> GetRolesAsync(int page = 0, CancellationToken cancellationToken = default)
    {

        var roles = await _roleManager.Roles
            .Skip(page * 15)
            .Take(15)
            .ToListAsync(cancellationToken);
        return roles;
    }

    public async Task<RoleActionRequestDto> GetRoleAsync(Guid roleId)
    {
        var role = (await _roleManager.FindByIdAsync(roleId.ToString()));
        if (role == null)
            throw new AppException("نقش پیدا نشد", ApiResultStatusCode.NotFound);

        var roleDto = role.Adapt<RoleActionRequestDto>();
        roleDto.Permissions = (await _roleManager.GetClaimsAsync(role))
            .Where(c => c.Type == CustomClaimType.Permission)
            .Select(c => c.Value)
            .ToList();

        return roleDto;
    }

    public async Task<ApplicationRole> CreateRoleAsync(RoleActionRequestDto request)
    {
        if (request.EnglishName.IsNullOrEmpty())
            throw new AppException("لطفا نام انگلیسی را وارد کنید");
        var applicationRole = new ApplicationRole
        {
            EnglishName = request.EnglishName,
            PersianName = request.PersianName,
            Description = request.Description,
            Name = $"{request.EnglishName}"
        };
        var createRoleResult = await _roleManager.CreateAsync(applicationRole);
        if (!createRoleResult.Succeeded)
            throw new AppException(string.Join('|', createRoleResult.Errors));

        foreach (var claim in request.Permissions)
            await _roleManager.AddClaimAsync(applicationRole, new Claim(CustomClaimType.Permission, claim));
        return applicationRole;
    }

    public async Task<bool> EditRoleAsync(RoleActionRequestDto request)
    {
        if (request.EnglishName.IsNullOrEmpty())
            throw new AppException("لطفا نام انگلیسی را وارد کنید");
        var applicationRole = await _roleManager.FindByIdAsync(request.RoleId.ToString());
        if (applicationRole == null)
            throw new AppException("نقش پیدا نشد");

        applicationRole.EnglishName = request.EnglishName;
        applicationRole.PersianName = request.PersianName;
        applicationRole.Description = request.Description;
        applicationRole.Name = $"{request.EnglishName}";

        var createRoleResult = await _roleManager.UpdateAsync(applicationRole);
        if (!createRoleResult.Succeeded)
            throw new AppException(string.Join('|', createRoleResult.Errors));
        var roleClaims = (await _roleManager.GetClaimsAsync(applicationRole)).Where(c => c.Type == CustomClaimType.Permission).ToList();
        foreach (var roleClaim in roleClaims.ToList())
        {
            if (request.Permissions.Contains(roleClaim.Value))
            {
                roleClaims.Remove(roleClaim);
                request.Permissions.Remove(roleClaim.Value);
            }
        }

        foreach (var claim in request.Permissions)
            await _roleManager.AddClaimAsync(applicationRole, new Claim(CustomClaimType.Permission, claim));

        foreach (var claim in roleClaims)
            await _roleManager.RemoveClaimAsync(applicationRole, claim);

        return true;
    }

    public async Task<bool> RemoveRoleAsync(Guid roleId)
    {
        var applicationRole = await _roleManager.FindByIdAsync(roleId.ToString());
        if (applicationRole == null)
            throw new AppException("User not found", ApiResultStatusCode.NotFound);
        var removeResult = await _roleManager.DeleteAsync(applicationRole);
        if (!removeResult.Succeeded)
            throw new AppException(string.Join('|', removeResult.Errors));
        return true;
    }

}