using System.Threading;
using Brizco.Domain.Entities.Complexes;
using Brizco.Domain.Entities.ShiftPlans;
using Brizco.Domain.Entities.Users;
using NPOI.SS.Formula.Functions;

namespace Brizco.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 IComplexService _complexService;
    private readonly IUserService _userService;

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



    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<bool> CheckPositionPermission(string permission, CancellationToken cancellationToken=default)
    {
        if (_currentUserService.UserId == null)
            throw new BaseApiException(ApiResultStatusCode.BadRequest, "User id is wrong");
        if (!Guid.TryParse(_currentUserService.UserId, out Guid userId))
            throw new BaseApiException(ApiResultStatusCode.BadRequest, "User id is wrong");
        if (_currentUserService.ComplexId == null)
            throw new BaseApiException(ApiResultStatusCode.BadRequest, "Complex id is wrong");
        if(!Guid.TryParse(_currentUserService.ComplexId,out Guid complexId))
            throw new BaseApiException(ApiResultStatusCode.BadRequest, "Complex id is wrong");

        var query = from shiftPlan in _repositoryWrapper.SetRepository<ShiftPlan>().Entities
            join shift in _repositoryWrapper.SetRepository<Shift>().Entities on shiftPlan.ShiftId equals shift.Id
            where shiftPlan.PlanFor.Date == DateTime.Today.Date && shiftPlan.ComplexId == complexId &&
                  shift.EndAt >= DateTime.Now.TimeOfDay && shift.StartAt <= DateTime.Now.TimeOfDay
            select shiftPlan;

        var currentShiftPlan = await query.FirstOrDefaultAsync(cancellationToken);
        if (currentShiftPlan == null)
            throw new BaseApiException(ApiResultStatusCode.BadRequest, "No active shift plan");

        var userCurrentPositionPermissions =
             await (from positionPermission in _repositoryWrapper.SetRepository<PositionPermission>().Entities
                join position in _repositoryWrapper.SetRepository<Position>().Entities on positionPermission.PositionId
                    equals position.Id
                join shiftPlaneUser in _repositoryWrapper.SetRepository<ShiftPlanUser>().Entities on position.Id equals
                    shiftPlaneUser.PositionId
                where shiftPlaneUser.ShiftPlanId == currentShiftPlan.Id && shiftPlaneUser.UserId == userId
                select positionPermission).ToListAsync(cancellationToken);

        return userCurrentPositionPermissions.Any(f => f.Permission == permission);
    }

    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 _userService.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, ComplexUserRoleSDto>> 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, ComplexUserRoleSDto>> 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,ComplexUserRoleSDto>> CompleteComplexSignUpAsync(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.ComplexCreated)
            throw new AppException("شما یک بار ثبت نام مجموعه خود را انجام داده اید");

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




        user.FirstName = requestDto.FirstName;
        user.LastName = requestDto.LastName;
        user.SignUpStatus = SignUpStatus.ComplexCreated;
        var result = await _userManager.UpdateAsync(user);
        if (!result.Succeeded)
            throw new AppException(string.Join('|', result.Errors.Select(e => e.Description)));

        var complex = await _complexService.CreateComplexAsync(requestDto.ComplexName,
            requestDto.ComplexAddress,
            requestDto.SupportPhoneNumber,
            user.Id,
            cancellationToken);

        return await CompleteLogin(user, cancellationToken);
    }
    
    private async Task<AccessToken<ApplicationUserSDto,ComplexUserRoleSDto>> CompleteLogin(ApplicationUser user, CancellationToken cancellationToken)
    {
        AccessToken<ApplicationUserSDto,ComplexUserRoleSDto> jwt;
        if (user.SelectedComplexUserRoleId != Guid.Empty)
        {
            var userComplexRoles = await _userService.GetUserRolesAsync(user.Id, cancellationToken);
            var complexUserRole = userComplexRoles.FirstOrDefault(c => c.Id == user.SelectedComplexUserRoleId);
            if (complexUserRole == null)
            {
                complexUserRole = userComplexRoles.FirstOrDefault();
                user.SelectedComplexUserRoleId = complexUserRole!.Id;
                await _userManager.UpdateAsync(user);
            }
            var complexUser = await _repositoryWrapper.SetRepository<ComplexUser>()
                .TableNoTracking
                .Where(c => c.Id == complexUserRole!.ComplexUserId)
                .Select(ComplexUserMapper.ProjectToSDto)
                .FirstOrDefaultAsync( cancellationToken);

            jwt = await _jwtService.Generate<ApplicationUserSDto, ApplicationUser, ComplexUserRoleSDto>(user, complexUser!.ComplexId, complexUserRole!.RoleId);
            jwt.User.SelectedComplexName = complexUser.ComplexName;
            jwt.User.SelectedRoleName = complexUserRole.RoleName;
            jwt.User.SelectedRoleId = complexUserRole!.Id;
            jwt.Roles = userComplexRoles;
        }
        else
        {
            var complexUser = await _repositoryWrapper.SetRepository<ComplexUser>()
                .TableNoTracking
                .Where(mcu => mcu.UserId == user.Id)
                .OrderByDescending(o => o.CreatedAt)
                .Select(ComplexUserMapper.ProjectToSDto)
                .FirstOrDefaultAsync(cancellationToken);
            if (complexUser == null)
                return (await _jwtService.Generate<ApplicationUserSDto, ApplicationUser>(user)).Adapt<AccessToken<ApplicationUserSDto, ComplexUserRoleSDto>>();

            var complexUserRole = await _repositoryWrapper.SetRepository<ComplexUserRole>()
                .TableNoTracking
                .Where(c => c.ComplexUserId == complexUser.Id)
                .OrderByDescending(o => o.CreatedAt)
                .Select(ComplexUserRoleMapper.ProjectToSDto)
                .FirstOrDefaultAsync(cancellationToken);



            if (complexUserRole != null)
            {

                user.SelectedComplexUserRoleId = complexUserRole.Id;
                await _userManager.UpdateAsync(user);
                jwt = (await _jwtService.Generate<ApplicationUserSDto, ApplicationUser>(user, complexUser.ComplexId, complexUserRole.RoleId)).Adapt<AccessToken<ApplicationUserSDto, ComplexUserRoleSDto>>();
                jwt.User.SelectedComplexName = complexUser.ComplexName;
                jwt.User.SelectedRoleName = complexUserRole.RoleName;
                jwt.User.SelectedRoleId = complexUserRole!.Id;
                jwt.Roles = new List<ComplexUserRoleSDto> { complexUserRole };
            }
            else
                jwt = (new AccessToken<ApplicationUserSDto, ComplexUserRoleSDto>()).Adapt<AccessToken<ApplicationUserSDto, ComplexUserRoleSDto>>();

        }

        return jwt;
    }



}