using Netina.Common.Extensions;
using Netina.Common.Models.Api;
using Netina.Common.Models.Exception;
using Netina.Core.Abstracts;
using Netina.Core.BaseServices.Abstracts;
using Netina.Core.CoreServices.Abstracts;
using Netina.Core.EntityServices.Abstracts;
using Netina.Domain.Dtos.RequestDtos;
using Netina.Domain.Dtos.ResponseDtos;
using Netina.Domain.Dtos.SmallDtos;
using Netina.Domain.Entities.Users;
using Netina.Domain.Enums;
using Netina.Repository.Abstracts;
using Netina.Repository.Repositories.Base.Contracts;

namespace Netina.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 IRepositoryWrapper _repositoryWrapper;
    private readonly ISmsService _smsService;
    private readonly IUserService _managerUserService;

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



    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<VerifyCodeResponseDto> 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 _managerUserService.CreateUserAsync(phoneNumber);

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

    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 (user.SignUpStatus == SignUpStatus.SignUpCompleted)
            throw new AppException("شما یک بار ثبت نام مجموعه خود را انجام داده اید");

        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;
        var result = await _userManager.UpdateAsync(user);
        if (!result.Succeeded)
            throw new AppException(string.Join('|', result.Errors.Select(e => e.Description)));
        var roleResult = await _userManager.AddToRoleAsync(user, "Customer");
        if (!roleResult.Succeeded)
            throw new AppException(string.Join('|', roleResult.Errors.Select(e => e.Description)));

        _repositoryWrapper.SetRepository<Customer>()
            .Add(new Customer
            {
                UserId = user.Id,
            });
        await _repositoryWrapper.SaveChangesAsync(default);

        return await CompleteLogin(user, cancellationToken);
    }


    private async Task<AccessToken<ApplicationUserSDto>> CompleteLogin(ApplicationUser user, CancellationToken cancellationToken)
    {
        AccessToken<ApplicationUserSDto> jwt;
        var role = await _userManager.GetRolesAsync(user);
        jwt = await _jwtService.Generate<ApplicationUserSDto, ApplicationUser>(user, role.ToList());
        jwt.User.RoleName = jwt.RoleName;
        return jwt;
    }



}