package com.cpop.mall.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.bean.BeanUtils;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.entity.loginInfo.MallStaffLoginInfo;
import com.cpop.core.base.entity.loginInfo.OamStaffLoginInfo;
import com.cpop.core.base.enums.InitRoleEnum;
import com.cpop.core.base.enums.SourceType;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.base.table.SysUser;
import com.cpop.core.service.CoreService;
import com.cpop.core.service.RedisService;
import com.cpop.core.utils.*;
import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.core.utils.uuid.IdUtils;
import com.cpop.mall.business.bo.ModifyUserPasswordBo;
import com.cpop.mall.business.bo.StaffBo;
import com.cpop.mall.business.bo.StaffPageBo;
import com.cpop.mall.business.entity.RoleBrand;
import com.cpop.mall.business.service.RoleBrandService;
import com.cpop.mall.business.vo.StaffInfoVo;
import com.cpop.mall.business.vo.StaffPageVo;
import com.cpop.system.business.entity.Brand;
import com.cpop.system.business.service.BrandService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.DbChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.cpop.mall.business.entity.Staff;
import com.cpop.mall.business.mapper.StaffMapper;
import com.cpop.mall.business.service.StaffService;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

import static com.cpop.core.base.table.table.SysUserTableDef.SYS_USER;
import static com.cpop.mall.business.entity.table.RoleBrandTableDef.ROLE_BRAND;
import static com.cpop.mall.business.entity.table.StaffTableDef.STAFF;
import static com.cpop.system.business.entity.table.BrandTableDef.BRAND;
import static com.cpop.system.business.entity.table.RoleTableDef.ROLE;
import static com.mybatisflex.core.query.QueryMethods.distinct;
import static com.mybatisflex.core.query.QueryMethods.groupConcat;

/**
 * 员工表 服务层实现。
 *
 * @author DB
 * @since 2023-10-19
 */
@Service("staffService")
public class StaffServiceImpl extends ServiceImpl<StaffMapper, Staff> implements StaffService {

    /**
     * @descriptions 查询员工分页列表
     * @author DB
     * @date 2023/10/20 11:12
     * @param bo 请求参数
     * @return: com.mybatisflex.core.paginate.Page<com.cpop.mall.business.vo.StaffPageVo>
     */
    @Override
    public Page<StaffPageVo> getStaffPageList(StaffPageBo bo) {
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        return this.mapper.paginateAs(pageDomain.getPageNum(), pageDomain.getPageSize()
                , QueryWrapper.create()
                        //去重
                        .select(distinct(STAFF.ALL_COLUMNS))
                        .select(SYS_USER.USER_NAME, SYS_USER.NICK_NAME, SYS_USER.EMAIL, SYS_USER.PHONE_NUMBER, SYS_USER.SEX, SYS_USER.AVATAR, SYS_USER.STATUS, SYS_USER.PASSWORD)
                        .select(ROLE.ROLE_NAME)
                        .select(ROLE_BRAND.BRAND_ID)
                        .select(BRAND.BRAND_NAME)
                        .from(STAFF)
                        .leftJoin(SYS_USER).on(SYS_USER.ID.eq(STAFF.USER_ID))
                        //关键中间表
                        .leftJoin(ROLE_BRAND).on(ROLE_BRAND.ID.eq(STAFF.ROLE_BRAND_ID))
                        .leftJoin(ROLE).on(ROLE.ID.eq(ROLE_BRAND.ROLE_ID))
                        .leftJoin(BRAND).on(BRAND.ID.eq(ROLE_BRAND.BRAND_ID))
                        //姓名
                        .and(STAFF.NAME.like(bo.getName()))
                        .and(SYS_USER.USER_NAME.ne(Constants.SUPER_ADMIN).or(SYS_USER.USER_NAME.isNull()))
                        //不查询超级管理员
                        .and(ROLE.ID.ne(InitRoleEnum.SUPER_MALL_ROLE.getId()).or(ROLE.ID.isNull()))
                        .groupBy(STAFF.ID)
                , StaffPageVo.class);
    }

    /**
     * @descriptions 新增员工
     * @author DB
     * @date 2023/10/20 11:24
     * @param bo 请求参数
     * @return: void
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertStaff(StaffBo bo) {
        //先添加用户信息
        SysUser sysUser;
        if (validatedUserInfo(bo)) {
            throw new ServiceException(MessageUtils.message("i18n_alert_userOrPhoneOrEmailIsExist"));
        } else {
            //用户名-手机-邮箱都需要做唯一校验
            sysUser = BeanUtils.mapToClass(bo, SysUser.class);
            //解密与重设密码
            decryptAndResetPasswords(sysUser);
            sysUser.setId(IdUtils.fastSimpleUUID());
            //获取当前创建人员信息
            LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
            sysUser.setCreateUserId(loginUser.getUserId());
            sysUser.setUpdateUserId(loginUser.getUserId());
            sysUser.setUserType(UserType.MALL_USER.toString());
            SpringUtils.getBean(CoreService.class).insertSysUser(sysUser);
        }
        //再添加员工信息
        RoleBrand roleBrand = new RoleBrand();
        Staff staff = new Staff();
        RoleBrandService roleBrandService = SpringUtils.getBean(RoleBrandService.class);
        //如果有传入品牌（超级管理员初始化管理账户）
        if (StringUtils.isNotBlank(bo.getBrandId())) {
            roleBrand.setBrandId(bo.getBrandId());
        } else {
            //获取当前用户信息
            JSONObject loginStaffInfo = SecurityUtils.getInstance().getLoginUserInfo();
            roleBrand.setBrandId(loginStaffInfo.getString("brandId"));
        }
        //获取品牌信息
        Brand brand = SpringUtils.getBean(BrandService.class).queryChain().where(BRAND.ID.eq(roleBrand.getBrandId())).one();
        roleBrand.setRoleId(bo.getRoleId());
        //设置中间表
        roleBrandService.save(roleBrand);
        staff.setUserId(sysUser.getId())
                .setName(bo.getName())
                .setRoleBrandId(roleBrand.getId())
                .setSourceType(brand.getSourceType());
        this.save(staff);
    }

    /**
     * @descriptions 修改员工
     * @author DB
     * @date 2023/10/20 11:36
     * @param bo 请求参数
     * @return: void
     */
    @Override
    public void updateStaff(StaffBo bo) {
        //先修改系统用户信息 用户名-手机-邮箱都需要做唯一校验
        if (validatedUserInfo(bo)) {
            throw new ServiceException(MessageUtils.message("i18n_alert_userOrPhoneOrEmailIsExist"));
        } else {
            SysUser sysUser = BeanUtils.mapToClass(bo, SysUser.class);
            sysUser.setId(bo.getUserId());
            //密码在这里不做修改
            sysUser.setPassword(null);
            sysUser.setUpdateUserId(SecurityUtils.getInstance().getLoginUser().getUserId());
            sysUser.setUpdateTime(LocalDateTime.now());
            SpringUtils.getBean(CoreService.class).updateSysUser(sysUser);
        }
        //角色品牌
        RoleBrandService roleBrandService = SpringUtils.getBean(RoleBrandService.class);
        RoleBrand roleBrand = roleBrandService.getById(bo.getRoleBrandId());
        roleBrand.setRoleId(bo.getRoleId());
        roleBrandService.updateById(roleBrand);
        //再修改员工信息
        Staff staff = new Staff();
        staff.setName(bo.getName())
                .setId(bo.getId());
        this.updateById(staff);
        //获取缓存信息
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        JSONObject jsonObject = redisService.getCacheObject(UserType.MINI_USER.getKey() + bo.getUserName());
        if (jsonObject != null) {
            LoginUser loginUser = jsonObject.getObject("user", LoginUser.class);
            MallStaffLoginInfo staffLoginInfo = BeanUtils.mapToClass(bo, MallStaffLoginInfo.class);
            loginUser.setUser(staffLoginInfo);
            redisService.setCacheObject(UserType.MINI_USER.getKey() + loginUser.getIdentification(), loginUser);
        }
    }

    /**
     * @Description: 用户名-手机-邮箱都需要做唯一校验
     * @param bo 请求参数
     * @return Boolean 通过/未通过
     * @Author DB
     * @Date: 2023/5/11 11:02
     **/
    private Boolean validatedUserInfo(StaffBo bo) {
        long count = this.count(QueryWrapper.create()
                //用户名
                .where(SYS_USER.USER_NAME.eq(bo.getUserName()))
                //手机号
                .or(SYS_USER.PHONE_NUMBER.eq(bo.getPhoneNumber()))
                .and(SYS_USER.USER_TYPE.eq(UserType.MALL_USER))
                .from(SYS_USER));
        if (StringUtils.isNotBlank(bo.getUserId())) {
            return count > 1;
        } else {
            return count > 0;
        }
    }

    /**
     * @Description: 解密与重设密码
     * @param sysUser 系统用户
     * @Author: DB
     * @Date: 2023/5/11 11:03
     **/
    private void decryptAndResetPasswords(SysUser sysUser) {
        //先用rsa解密
        String password = SpringUtils.getBean(RsaUtils.class).decrypt(sysUser.getPassword());
        //再加密
        sysUser.setPassword(SpringUtils.getBean(PasswordEncoder.class).encode(password));
    }

    /**
     * @descriptions 删除员工
     * @author DB
     * @date 2023/10/20 11:44
     * @param id 主键
     * @return: void
     */
    @Override
    public void removeStaffById(String id) {
        //先获取员工
        Staff staff = this.getById(id);
        if (null == staff) {
            throw new ServiceException("获取信息失败,请联系相关人员");
        }
        //删除用户
        SpringUtils.getBean(CoreService.class).removeSysUserById(staff.getUserId());
        //删除中间表
        SpringUtils.getBean(RoleBrandService.class).removeById(staff.getRoleBrandId());
        //删除员工
        this.removeById(id);
    }

    /**
     * @descriptions 用户名是否存在
     * @author DB
     * @date 2023/10/20 11:55
     * @param username 用户名
     * @param id 主键
     * @return: void
     */
    @Override
    public void isAccountExist(String username, String id) {
        if (StringUtils.isBlank(username)){
            return;
        }
        if (this.count(QueryWrapper.create()
                .where(SYS_USER.USER_NAME.eq(username))
                .and(SYS_USER.USER_TYPE.eq(UserType.MALL_USER))
                .and(SYS_USER.ID.ne(id))) > 0) {
            throw new ServiceException(MessageUtils.message("i18n_alert_userIsExist"));
        }
    }

    /**
     * @descriptions 获取员工信息
     * @author DB
     * @date 2023/10/20 12:00
     * @param id 主键
     * @return: com.cpop.mall.business.vo.StaffInfoVo
     */
    @Override
    public StaffInfoVo getStaffInfo(String id) {
        return this.getOneAs(QueryWrapper.create()
                        .select(STAFF.ALL_COLUMNS)
                        .select(SYS_USER.USER_NAME, SYS_USER.NICK_NAME, SYS_USER.SEX, SYS_USER.PHONE_NUMBER, SYS_USER.ID.as(StaffInfoVo::getUserId))
                        .select(ROLE_BRAND.ROLE_ID)
                        .select(ROLE.ROLE_NAME)
                        .from(STAFF)
                        .leftJoin(ROLE_BRAND).on(ROLE_BRAND.ID.eq(STAFF.ROLE_BRAND_ID))
                        .leftJoin(SYS_USER).on(SYS_USER.ID.eq(STAFF.USER_ID))
                        .leftJoin(ROLE).on(ROLE.ID.eq(ROLE_BRAND.ROLE_ID))
                        .where(STAFF.ID.eq(id))
                , StaffInfoVo.class);
    }

    /**
     * @descriptions 修改系统用户密码
     * @author DB
     * @date 2023/10/20 12:04
     * @param bo 请求体
     * @return: void
     */
    @Override
    public void modifyUserPassword(ModifyUserPasswordBo bo) {
        //只允许超级管理员或自己修改面膜
        JSONObject loginStaffInfo = SecurityUtils.getInstance().getLoginUserInfo();
        String userName = loginStaffInfo.getString("userName");
        //同数据库密码进行比较
        SysUser user = DbChain.table(SYS_USER)
                .where(SYS_USER.ID.eq(bo.getUserId()))
                .oneAs(SysUser.class);
        if (!StringUtils.equals(userName, Constants.SUPER_ADMIN) || !StringUtils.equals(userName, user.getUserName())) {
            throw new ServiceException("非超级管理员不允许修改他人密码");
        }
        //先用rsa解密
        RsaUtils rsaUtils = SpringUtils.getBean(RsaUtils.class);
        String oldPassword = rsaUtils.decrypt(bo.getOldPassword());
        if (BCrypt.checkpw(oldPassword, user.getPassword())) {
            //存入系统
            DbChain.table(SYS_USER)
                    .set(SYS_USER.PASSWORD,SpringUtils.getBean(PasswordEncoder.class).encode(rsaUtils.decrypt(bo.getNewPassword())))
                    .where(SYS_USER.ID.eq(bo.getUserId()))
                    .update();
        } else {
            throw new ServiceException(MessageUtils.message("i18n_alert_oldPasswordIsWrong"));
        }
    }
}
