package com.kym.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.Digester;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.kym.common.R;
import com.kym.common.constant.ResponseEnum;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.CommUtil;
import com.kym.common.utils.IDGenerator;
import com.kym.entity.AdminUser;
import com.kym.entity.AdminUserRole;
import com.kym.entity.InvestorInfo;
import com.kym.entity.Role;
import com.kym.entity.common.PageBean;
import com.kym.entity.queryParams.CommonQueryParam;
import com.kym.entity.vo.AdminUserVo;
import com.kym.mapper.AdminUserMapper;
import com.kym.service.AdminUserRoleService;
import com.kym.service.AdminUserService;
import com.kym.service.InvestorInfoService;
import com.kym.service.RoleService;
import com.kym.service.mybatisplus.MyBaseServiceImpl;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;


/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author skyline
 * @since 2023-07-11
 */
@Service
public class AdminUserServiceImpl extends MyBaseServiceImpl<AdminUserMapper, AdminUser> implements AdminUserService {
    final static Digester MD5 = new Digester(DigestAlgorithm.MD5);
    private static final Logger LOGGER = LoggerFactory.getLogger(AdminUserServiceImpl.class);
    private final InvestorInfoService investorInfoService;
    private final RoleService roleService;
    @Value("${password.privateKey}")
    private String privateKey;
    @Value("${password.publicKey}")
    private String publicKey;
    @Resource
    private AdminUserRoleService adminUserRoleService;

    public AdminUserServiceImpl(InvestorInfoService investorInfoService, RoleService roleService) {
        this.investorInfoService = investorInfoService;
        this.roleService = roleService;
    }

    /**
     * 获取投资者角色对应的adminUser用户
     */
    @Override
    public List<AdminUser> getInvestorAdminUsers() {
        // 投资者角色ID
        var investorRole = roleService.lambdaQuery()
                .eq(Role::getRoleName, "投资者")
                .eq(Role::getRoleDesc, "investor").one();
        // 查找所有角色为investor的adminUser用户
        MPJLambdaWrapper<AdminUser> wrapper = JoinWrappers.lambda(AdminUser.class)
                .selectAll(AdminUser.class)
                .leftJoin(AdminUserRole.class, AdminUserRole::getAdminUserId, AdminUser::getId)
                .eq(AdminUserRole::getRoleId, investorRole.getId());
        return this.selectJoinList(AdminUser.class, wrapper);

    }

    @Override
    public R<?> login(String mobilePhone, String password) {
        var user = lambdaQuery().eq(AdminUser::getMobilePhone, mobilePhone).one();
        if (user == null) {
            return R.failed(ResponseEnum.LOGIN_FAILED);
        }
        if (user.getStatus() == AdminUser.STATUS_禁用) {
            throw new BusinessException("用户状态异常，无法登录");
        }
        RSA rsa = new RSA(privateKey, publicKey);
        password = rsa.decryptStr(password, KeyType.PrivateKey, StandardCharsets.UTF_8);
        var pwd = MD5.digestHex(password.concat(MD5.digestHex(mobilePhone).substring(0, 5)));
        if (pwd.equals(user.getPassword())) {
            StpUtil.login(user.getId());
            // 用户名存入session，统一日志读取使用
            StpUtil.getSession().set("mobilePhone", user.getMobilePhone());
            StpUtil.getSession().set("username", user.getUsername());
            user.setLastLoginTime(LocalDateTime.now());
            this.updateById(user);
            LOGGER.info("操作员：" + mobilePhone + "/id:" + user.getId() + "登录");
            // token在cookie中有隐式返回，这里多做显式返回
            return R.success(Map.of("id", user.getId(), "accessToken", StpUtil.getTokenValue()));
        }
        return R.failed(ResponseEnum.LOGIN_FAILED);
    }

    @Override
    public void logout(Long id) {
        StpUtil.logout(id);
    }

    /**
     * 用户信息、权限、角色
     *
     * @return
     */
    @Override
    public Object profile() {
        long userId = StpUtil.getLoginIdAsLong();
        return listUserPermissions(userId);
    }

    @Override
    public List<AdminUserVo> listUserPermissions(Long loginId) {
        return baseMapper.listUserPermissions(loginId);
    }

    @Override
    public List<String> listUserRoles(Long loginId) {
        return baseMapper.listUserRoles(loginId);
    }

    @Override
    public PageBean<AdminUserVo> listAdminUser(CommonQueryParam params) {
        PageHelper.startPage(params.getPageNum(), params.getPageSize());
        var list = baseMapper.listAdminUser(params);
        return new PageBean<>(list);
    }

    @Override
    public String createAdminUser(AdminUserVo adminUserVo) {
        var password = MD5.digestHex(adminUserVo.getPassword().concat(MD5.digestHex(adminUserVo.getMobilePhone()).substring(0, 5)));
        // 用户
        var adminUser = new AdminUser();
        BeanUtils.copyProperties(adminUserVo, adminUser);
        adminUser.setId(IDGenerator.INS().nextId());
        adminUser.setPassword(password);
        adminUser.setCreateTime(LocalDateTime.now());
        save(adminUser);
        // 角色和权限
        var adminUserRole = new AdminUserRole()
                .setAdminUserId(adminUser.getId())
                .setRoleId(adminUserVo.getRoleId());
        adminUserRoleService.save(adminUserRole);
        return String.valueOf(adminUser.getId());
    }

    @Override
    public Object detail(long id) {
        AdminUser adminUser = baseMapper.selectById(id);
        adminUser.setPassword(null);
        List<AdminUserRole> userRoleList = adminUserRoleService.lambdaQuery().eq(AdminUserRole::getAdminUserId, id).list();
        return Map.of("adminUser", adminUser, "roles", userRoleList);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateRole(long userId, List<Long> roleIdList) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("admin_user_id", userId);
        adminUserRoleService.remove(wrapper);
        if (CommUtil.isEmptyOrNull(roleIdList)) {
            return;
        }
        roleIdList.forEach(roleId -> {
            AdminUserRole roleEntity = new AdminUserRole();
            roleEntity.setAdminUserId(userId);
            roleEntity.setRoleId(roleId);
            roleEntity.setCreateTime(LocalDateTime.now());
            roleEntity.setUpdateTime(LocalDateTime.now());
            adminUserRoleService.save(roleEntity);
        });

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateAdminUser(AdminUserVo adminUserVo) {
        AdminUser adminUser = new AdminUser();
        if (adminUserVo.getStatus() == AdminUser.STATUS_禁用) {
            adminUser.setStatus(AdminUser.STATUS_禁用);
            investorInfoService.lambdaUpdate().set(InvestorInfo::getStatus, InvestorInfo.STATUS_无效).eq(InvestorInfo::getAdminUserId, adminUserVo.getId()).update();
        } else {
            adminUser.setStatus(AdminUser.STATUS_启用);
            investorInfoService.lambdaUpdate().set(InvestorInfo::getStatus, InvestorInfo.STATUS_有效).eq(InvestorInfo::getAdminUserId, adminUserVo.getId()).update();
        }
        BeanUtil.copyProperties(adminUserVo, adminUser);
        baseMapper.updateById(adminUser);
    }

    @Override
    public PageBean<?> listUser(CommonQueryParam params) {
        PageHelper.startPage(params.getPageNum(), params.getPageSize());
        List<AdminUser> list = lambdaQuery()
                .like(!CommUtil.isEmptyOrNull(params.getUsername()), AdminUser::getUsername, params.getUsername())
                .like(!CommUtil.isEmptyOrNull(params.getMobilePhone()), AdminUser::getMobilePhone, params.getMobilePhone())
                .like(params.getStatus() != null, AdminUser::getStatus, params.getStatus())
                .orderByDesc(AdminUser::getId)
                .list();
        return new PageBean<>(list);
    }

    @Override
    public Object wxLogin(AdminUser user) {
        CommUtil.asserts(CommUtil.isNotEmptyAndNull(user.getOpenId()), "微信账户信息错误");

        var adminUser = lambdaQuery()
                .eq(AdminUser::getOpenId, user.getOpenId())
                .eq(AdminUser::getMobilePhone, user.getMobilePhone())
                .eq(AdminUser::getStatus, AdminUser.STATUS_启用).one();
        if (null == adminUser) {
            //判断是否已有手机账号存在,有则自动绑定union id,无则创建账号
            adminUser = lambdaQuery()
                    .eq(AdminUser::getMobilePhone, user.getMobilePhone())
                    .eq(AdminUser::getStatus, AdminUser.STATUS_启用).one();
            if (null != adminUser) {
                adminUser.setOpenId(user.getOpenId());
            } else {
                //自动注册
                CommUtil.asserts(CommUtil.isNotEmptyAndNull(user.getMobilePhone()), "手机号不能为空");
                adminUser = new AdminUser();
                adminUser.setId(IDGenerator.INS().nextId());
                adminUser.setOpenId(user.getOpenId());
                adminUser.setStatus(AdminUser.STATUS_启用);
                adminUser.setMobilePhone(user.getMobilePhone());
                adminUser.setUsername(user.getMobilePhone());
                adminUser.setLastLoginTime(LocalDateTime.now());
                adminUser.setUpdateTime(LocalDateTime.now());
                this.save(adminUser);
            }
        }
        StpUtil.login(adminUser.getId());
        // 用户名存入session，统一日志读取使用
        StpUtil.getSession().set("mobilePhone", adminUser.getMobilePhone());
        StpUtil.getSession().set("username", adminUser.getUsername());
        adminUser.setLastLoginTime(LocalDateTime.now());
        adminUser.setMobilePhone(adminUser.getMobilePhone());
        this.updateById(adminUser);
        LOGGER.info("微信用户union:{},name:{},phone:{},id:{}登录", adminUser.getOpenId(), adminUser.getUsername(), adminUser.getMobilePhone(), adminUser.getId());
        // token在cookie中有隐式返回，这里多做显式返回
        adminUser.setPassword(null);
        adminUser.setOpenId(null);
        return Map.of("user", adminUser, "satoken", StpUtil.getTokenValue());
    }

    @Override
    public Object loginByOpenId(AdminUser user) {
        CommUtil.asserts(CommUtil.isNotEmptyAndNull(user.getOpenId()), "微信账户信息错误");
        var adminUser = lambdaQuery()
                .eq(AdminUser::getOpenId, user.getOpenId())
                .eq(AdminUser::getStatus, AdminUser.STATUS_启用).one();
        if (null == adminUser) {
            return Collections.emptyMap();
        } else {
            StpUtil.login(adminUser.getId());
            // 用户名存入session，统一日志读取使用
            StpUtil.getSession().set("mobilePhone", adminUser.getMobilePhone());
            StpUtil.getSession().set("username", adminUser.getUsername());
            adminUser.setLastLoginTime(LocalDateTime.now());
            this.updateById(adminUser);
            adminUser.setPassword(null);
            adminUser.setOpenId(null);
            return Map.of("user", adminUser, "satoken", StpUtil.getTokenValue());
        }
    }
}
