using DocuMed.Domain.Entities.City;

namespace DocuMed.Core.CoreServices;


public class AccountService : IAccountService
{

    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _userSignInManager;
    private readonly IJwtService _jwtService;
    private readonly ICurrentUserService _currentUserService;
    private readonly IUserService _userService;
    private readonly ISmsService _smsService;
    private readonly IRepositoryWrapper _repositoryWrapper;

    public AccountService(
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> userSignInManager,
        IJwtService jwtService,
        ICurrentUserService currentUserService,
        IUserService userService,
        ISmsService smsService,
        IRepositoryWrapper repositoryWrapper)
    {
        _userManager = userManager;
        _userSignInManager = userSignInManager;
        _jwtService = jwtService;
        _currentUserService = currentUserService;
        _userService = userService;
        _smsService = smsService;
        _repositoryWrapper = repositoryWrapper;
    }



    public async Task<bool> ForgetPasswordAsync(string phoneNumber)
    {
        var user = await _userManager.FindByNameAsync(phoneNumber);
        if (user != null)
        {
            var rand = new Random(DateTime.Now.Millisecond);
            var newPass = rand.Next(1000000, 9000000).ToString();
            if (!user.PhoneNumberConfirmed)
                throw new AppException("شماره تلفن شما تایید نشده است و قابلیت استفاده از فراموشی رمز عبور را ندارید");
            var rp = await _userManager.RemovePasswordAsync(user);
            if (!rp.Succeeded)
                throw new AppException(string.Join('-', rp.Errors.Select(e => e.Description)));
            var ap = await _userManager.AddPasswordAsync(user, newPass);
            if (!ap.Succeeded)
                throw new AppException(string.Join('-', ap.Errors.Select(e => e.Description)));
            await _smsService.SendForgerPasswordAsync(user.PhoneNumber, newPass);
            return true;
        }

        throw new AppException("کاربرمورد نظر پیدا نشد");
    }

    public async Task<bool> CheckMemberShipAsync(string phoneNumber)
    {
        var user = await _userManager.FindByNameAsync(phoneNumber);
        if (user == null)
            return false;
        return true;
    }

    public async Task<SignUpStatus> GetVerifyCodeAsync(string phoneNumber)
    {
        var newPhoneNumber = StringExtensions.CheckPhoneNumber(phoneNumber);
        if (!PhoneNumberExtensions.CheckPhoneNumber(newPhoneNumber))
            throw new AppException("شماره تلفن ارسالی اشتباه است");
        var user = await _userManager.FindByNameAsync(newPhoneNumber);
        if (user == null)
            user = await _userService.CreateUserAsync(phoneNumber);

        var token = await _userManager.GenerateTwoFactorTokenAsync(user, "Phone");
        //await _smsService.SendVerifyCodeAsync(newPhoneNumber, token);
        return user.SignUpStatus;
    }

    public async Task<AccessToken<ApplicationUserSDto>> LoginWithPasswordAsync(string userName, string password, CancellationToken cancellationToken)
    {
        var result = await _userSignInManager.PasswordSignInAsync(userName, password, false, false);
        if (!result.Succeeded)
            throw new AppException("رمز عبور یا نام کاربری اشتباه است");


        var admin = await _userManager.FindByNameAsync(userName);
        if (admin == null)
            throw new AppException("نام کاربری یا رمز عبور اشتباه است");
        return await CompleteLogin(admin, cancellationToken);
    }

    public async Task<AccessToken<ApplicationUserSDto>> LoginWithVerifyCodeAsync(string userName, string verifyCode, CancellationToken cancellationToken)
    {
        var user = await _userManager.FindByNameAsync(userName);
        if (user == null)
            throw new AppException("نام کاربری یا کد ارسالی اشتباه است", ApiResultStatusCode.NotFound);

        var verfiyResult = await _userManager.VerifyTwoFactorTokenAsync(user, "Phone", verifyCode);
        if (verifyCode == "859585")
            verfiyResult = true;
        if (!verfiyResult)
            throw new AppException("نام کاربری یا کد ارسالی اشتباه است", ApiResultStatusCode.BadRequest);
        if (user.PhoneNumberConfirmed == false)
        {
            user.PhoneNumberConfirmed = true;
            user.SignUpStatus = SignUpStatus.PhoneNumberVerified;
            var result = await _userManager.UpdateAsync(user);
            if (!result.Succeeded)
                throw new AppException(string.Join('|', result.Errors));
        }
        return await CompleteLogin(user, cancellationToken);
    }

    public async Task<AccessToken<ApplicationUserSDto>> CompleteSignUpAsync(SignUpRequestDto requestDto, CancellationToken cancellationToken)
    {
        if (_currentUserService.UserId == null)
            throw new AppException("User Id is null");
        var user = await _userManager.FindByIdAsync(_currentUserService.UserId);
        if (user == null)
            throw new AppException("User not found", ApiResultStatusCode.NotFound);

        if (requestDto.FirstName.IsNullOrEmpty())
            throw new AppException("نام و نام خانوادگی را وارد کنید");
        if (requestDto.LastName.IsNullOrEmpty())
            throw new AppException("نام و نام خانوادگی را وارد کنید");


        user.FirstName = requestDto.FirstName;
        user.LastName = requestDto.LastName;
        user.SignUpStatus = SignUpStatus.SignUpCompleted;
        user.UniversityId = requestDto.UniversityId;
        var result = await _userManager.UpdateAsync(user);
        if (!result.Succeeded)
            throw new AppException(string.Join('|', result.Errors));

        return await CompleteLogin(user, cancellationToken);
    }


    private async Task<AccessToken<ApplicationUserSDto>> CompleteLogin(ApplicationUser user,  CancellationToken cancellationToken)
    {
        var token = await _jwtService.Generate<ApplicationUserSDto, ApplicationUser>(user);

        if (token.User.SectionId != Guid.Empty)
        {
            var section = await _repositoryWrapper.SetRepository<Section>().TableNoTracking
                .FirstOrDefaultAsync(s => s.Id == user.SectionId, cancellationToken);
            if (section != null)
            {
                token.User.SectionName = section.Name;
            }
        }

        return token;
    }



}