package com.jumeirah.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jumeirah.common.convert.UserConvert;
import com.jumeirah.common.entity.MerchantPermission;
import com.jumeirah.common.entity.MerchantUser;
import com.jumeirah.common.enums.StateEnum;
import com.jumeirah.common.mapper.MerchantUserMapper;
import com.jumeirah.common.param.MerchantLoginParam;
import com.jumeirah.common.param.MerchantUserPageParam;
import com.jumeirah.common.service.MerchantPermissionService;
import com.jumeirah.common.service.MerchantUserPermissionService;
import com.jumeirah.common.service.MerchantUserService;
import com.jumeirah.common.vo.LoginMerUserTokenVo;
import com.jumeirah.common.vo.MerchantUserQueryVo;
import io.geekidea.springbootplus.config.properties.JwtProperties;
import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
import io.geekidea.springbootplus.framework.common.api.ApiCode;
import io.geekidea.springbootplus.framework.common.api.ApiResult;
import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import io.geekidea.springbootplus.framework.shiro.cache.MerchantLoginRedisService;
import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
import io.geekidea.springbootplus.framework.shiro.jwt.realm.LoginClientTypeEnum;
import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
import io.geekidea.springbootplus.framework.shiro.util.SaltUtil;
import io.geekidea.springbootplus.framework.shiro.vo.LoginUserVo;
import io.geekidea.springbootplus.framework.util.PasswordUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 商家 服务实现类
 *
 * @author wei
 * @since 2020-09-28
 */
@Slf4j
@Service
public class MerchantUserServiceImpl extends BaseServiceImpl<MerchantUserMapper, MerchantUser> implements MerchantUserService {

    @Lazy
    @Autowired
    private SpringBootPlusProperties springBootPlusProperties;

    //    @Lazy
//    @Autowired
//    private RedisTemplate redisTemplate;
    @Lazy
    @Autowired
    private MerchantLoginRedisService merchantLoginRedisService;

    @Autowired
    private MerchantPermissionService merchantPermissionService;

    @Autowired
    private MerchantUserPermissionService merchantUserPermissionService;

    @Lazy
    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private MerchantUserMapper merchantUserMapper;
//
//    @Autowired
//    private MerchantRoleService merchantRoleService;
//
//    @Autowired
//    private MerchantService merchantService;
//
//    @Autowired
//    private MerchantSmsService merchantSmsService;

//    @Autowired
//    private MerchantRolePermissionService merchantRolePermissionService;

    public MerchantUser getMerUserByUsername(String username) throws Exception {
        MerchantUser sysUser = new MerchantUser().setUsername(username);
        return merchantUserMapper.selectOne(new QueryWrapper<MerchantUser>(sysUser));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<LoginMerUserTokenVo> login(MerchantLoginParam merchantLoginParam) throws Exception {

        String username = merchantLoginParam.getUsername();
        // 从数据库中获取登录用户信息
        MerchantUser merchantUser = getMerUserByUsername(username);
        if (merchantUser == null) {
            log.error("登录失败,用户名或密码错误merchantLoginParam:{}", merchantLoginParam);
            return ApiResult.result(ApiCode.PWD_OR_USERNAME_ERROR, null);
        }
        if (StateEnum.DISABLE.getCode().equals(merchantUser.getState())) {
            log.error("登录失败,禁用:{}", merchantLoginParam);

            return ApiResult.result(ApiCode.LOGIN_EXCEPTION, null);
        }

        // 实际项目中，前端传过来的密码应先加密
        // 原始密码明文：123456
        // 原始密码前端加密：sha256(123456)
        // 后台加密规则：sha256(sha256(123456) + salt)
        String encryptPassword = PasswordUtil.encrypt(merchantLoginParam.getPassword(), merchantUser.getSalt());
        if (!encryptPassword.equals(merchantUser.getPassword())) {
            return ApiResult.result(ApiCode.PWD_OR_USERNAME_ERROR, null);
        }

        // 将系统用户对象转换成登录用户对象
        LoginUserVo loginSysUserVo = UserConvert.INSTANCE.merchantUserToLoginSysUserVo(merchantUser);

//        // 获取当前用户角色
//        Long roleId = merchantUser.getRoleId();
//        MerchantRole merchantRole = merchantRoleService.getById(roleId);
//        if (merchantRole == null) {
//            throw new AuthenticationException("角色不存在");
//        }
//        if (StateEnum.DISABLE.getCode().equals(merchantRole.getState())) {
//            throw new AuthenticationException("角色已禁用");
//        }
//        loginSysUserVo.setRoleId(merchantRole.getId())
//                .setRoleName(merchantRole.getName())
//                .setRoleCode(merchantRole.getCode());


//        // 获取当前用户权限
//        Set<String> permissionCodes = merchantRolePermissionService.getPermissionCodesByRoleId(roleId);
//        loginSysUserVo.setPermissionCodes(permissionCodes);

        // 如果是超级管理员查询所有权限
        if (merchantUser.getIsAdmin().equals(1)) {
            List<MerchantPermission> merchantPermissions = merchantPermissionService.list();
            Set<String> permissionCodes = new HashSet<>();
            for (MerchantPermission merchantPermission : merchantPermissions) {
                permissionCodes.add(merchantPermission.getCode());
            }
            loginSysUserVo.setPermissionCodes(permissionCodes);
        } else {
            Set<String> permissionCodes = merchantUserPermissionService.getPermissionCodesByUserId(merchantUser.getId());
            loginSysUserVo.setPermissionCodes(permissionCodes);
        }

        // 获取数据库中保存的盐值
        String newSalt = SaltUtil.getSalt(merchantUser.getSalt(), jwtProperties);

        // 生成token字符串并返回
        Long expireSecond = jwtProperties.getExpireSecond();
        String token = JwtUtil.generateToken(username, newSalt, Duration.ofSeconds(expireSecond));
        log.debug("token:{}", token);

        // 创建AuthenticationToken
        JwtToken jwtToken = JwtToken.build(token, username, merchantUser.getId(), newSalt, expireSecond, LoginClientTypeEnum.MERCHANT.getType(), merchantUser.getMcId());

        boolean enableShiro = springBootPlusProperties.getShiro().isEnable();
        if (enableShiro) {
            // 从SecurityUtils里边创建一个 subject
            Subject subject = SecurityUtils.getSubject();
            // 执行认证登录
            subject.login(jwtToken);
        } else {
            log.warn("未启用Shiro");
        }

        // 缓存登录信息到Redis
        merchantLoginRedisService.cacheLoginInfo(jwtToken, loginSysUserVo);
        log.debug("登录成功,username:{}", username);

/*        // 缓存登录信息到redis
        String tokenSha256 = DigestUtils.sha256Hex(token);
        redisTemplate.opsForValue().set(tokenSha256, loginSysUserVo, 1, TimeUnit.DAYS);*/

        // 返回token和登录用户信息对象
        LoginMerUserTokenVo loginSysUserTokenVo = new LoginMerUserTokenVo();
        loginSysUserTokenVo.setToken(token);
        loginSysUserTokenVo.setLoginSysUserVo(loginSysUserVo);

        return ApiResult.ok(loginSysUserTokenVo);
    }

//    @Override
//    public ApiResult<Boolean> register(MerchantRegisterParam merchantRegisterPram) throws Exception {
//
//        // 校验验证码
//        boolean equalsRegisterCode = merchantSmsService.equalsRegisterCode(merchantRegisterPram.getPhoneArea(), merchantRegisterPram.getPhone(), merchantRegisterPram.getSmsCode());
//        if (!equalsRegisterCode) {
//            return ApiResult.fail(ApiCode.SMS_CODE_ERROR);
//        }
//        // 删除已使用的验证码
//        merchantSmsService.deleteRegisterCode(merchantRegisterPram.getPhoneArea(), merchantRegisterPram.getPhone());
//        // 判断是否已经注册
////            if (appUserService.hasUserByPhoneNumer(loginParam.getPhoneArea(), loginParam.getPhone())) {
////                // 如果已经注册直接走登陆的代码
////                return appUserService.login(loginParam, language, true);
////            }
//
//        // 没注册则先保存到数据库
//        Merchant merchant = new Merchant();
//        BeanUtils.copyProperties(merchantRegisterPram, merchant);
//        merchant.setAuditRegisterStatus(0);
//        merchant.setState(1);
//        boolean isDbOk = merchantService.saveMerchant(merchant);
//        if (!isDbOk) {
//            return ApiResult.fail(ApiCode.SPRING_BOOT_PLUS_EXCEPTION);
//        }
//
//        // 创建一个该公司默认的管理员账号
//
//        return ApiResult.ok();
//    }
//

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveMerchantUser(MerchantUser merchantUser) throws Exception {
        return super.save(merchantUser);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateMerchantUser(MerchantUser merchantUser) throws Exception {
        return super.updateById(merchantUser);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteMerchantUser(Long id) throws Exception {
        return super.removeById(id);
    }

    @Override
    public MerchantUserQueryVo getMerchantUserById(Long id) throws Exception {
        return merchantUserMapper.getMerchantUserById(id);
    }

    @Override
    public Paging<MerchantUserQueryVo> getMerchantUserPageList(MerchantUserPageParam merchantUserPageParam) throws Exception {
        Page<MerchantUserQueryVo> page = new PageInfo<>(merchantUserPageParam, OrderItem.desc(getLambdaColumn(MerchantUser::getCreateTime)));
        IPage<MerchantUserQueryVo> iPage = merchantUserMapper.getMerchantUserPageList(page, merchantUserPageParam);
        return new Paging<MerchantUserQueryVo>(iPage);
    }


}
