package com.cpop.oam.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.OamStaffLoginInfo;
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.jambox.framework.constant.JamboxCloudUrl;
import com.cpop.oam.business.bo.*;
import com.cpop.oam.business.entity.Staff;
import com.cpop.oam.business.entity.StaffMidDept;
import com.cpop.oam.business.mapper.StaffMapper;
import com.cpop.oam.business.service.StaffMidDeptService;
import com.cpop.oam.business.service.StaffService;
import com.cpop.oam.business.vo.StaffInfoVo;
import com.cpop.oam.business.vo.StaffPageVo;
import com.cpop.oam.business.vo.StaffVo;
import com.cpop.oam.business.vo.SysOperationLogVo;
import com.cpop.oam.framework.constant.OamConfigKey;
import com.cpop.oam.framework.enums.OamConfigEnum;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.row.DbChain;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.cpop.core.base.table.table.SysOperationLogTableDef.SYS_OPERATION_LOG;
import static com.cpop.core.base.table.table.SysUserTableDef.SYS_USER;
import static com.cpop.oam.business.entity.table.StaffMidDeptTableDef.STAFF_MID_DEPT;
import static com.cpop.oam.business.entity.table.StaffTableDef.STAFF;
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-09-07
 */
@Slf4j
@Service("oamStaffService")
public class StaffServiceImpl extends ServiceImpl<StaffMapper, Staff> implements StaffService {

    /**
     * @descriptions 查询员工分页列表
     * @author DB
     * @date 2023/09/07 18:11
     * @param bo 请求参数
     * @return com.mybatisflex.core.paginate.Page<com.jambox.system.business.vo.SysStaffPageVo>
     */
    @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)
                        //将部门id分组
                        .select(groupConcat(STAFF_MID_DEPT.DEPT_ID).as(StaffPageVo::getDeptId))
                        .from(STAFF)
                        .leftJoin(SYS_USER).on(SYS_USER.ID.eq(STAFF.USER_ID))
                        .leftJoin(ROLE).on(ROLE.ID.eq(STAFF.ROLE_ID))
                        //左联中间表
                        .leftJoin(STAFF_MID_DEPT).on(STAFF_MID_DEPT.STAFF_ID.eq(STAFF.ID))
                        //姓名
                        .and(STAFF.NAME.like(bo.getName()))
                        //员工类型
                        .and(STAFF.STAFF_TYPE.eq(bo.getStaffType()))
                        //部门id
                        .and(STAFF_MID_DEPT.DEPT_ID.eq(bo.getDeptId()))
                        .and(SYS_USER.USER_NAME.ne(Constants.SUPER_ADMIN).or(SYS_USER.USER_NAME.isNull()))
                        .groupBy(STAFF.ID)
                , StaffPageVo.class);
    }

    /**
     * @descriptions 新增员工
     * @author DB
     * @date 2023/09/08 14:12
     * @param bo 请求参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysUser insertStaff(StaffBo bo) {
        //先添加用户信息
        SysUser sysUser;
        if (validatedUserInfo(bo)) {
            throw new ServiceException(MessageUtils.message("i18n_alert_userOrPhoneOrEmailIsExist"));
        } else {
            //用户名-手机-邮箱都需要做唯一校验
            sysUser = BeanUtils.mapToClass(bo, SysUser.class);
            //解密与重设密码
            sysUser.setRsaPassword(bo.getPassword());
            decryptAndResetPasswords(sysUser);
            sysUser.setId(IdUtils.fastSimpleUUID());
            //获取当前创建人员信息
            LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
            if (loginUser == null){
                sysUser.setCreateUserId("1");
                sysUser.setUpdateUserId("1");
            } else {
                sysUser.setCreateUserId(loginUser.getUserId());
                sysUser.setUpdateUserId(loginUser.getUserId());
            }
            sysUser.setUserType(UserType.OAM_USER.toString());
            SpringUtils.getBean(CoreService.class).insertSysUser(sysUser);
        }
        //再添加员工信息
        Staff sysStaff = BeanUtils.mapToClass(bo, Staff.class);
        sysStaff.setUserId(sysUser.getId());
        this.save(sysStaff);
        //批量导入部门信息
        List<String> deptList = Arrays.asList(bo.getDeptId().split(","));
        List<StaffMidDept> staffMidDeptList = new ArrayList<>();
        deptList.forEach(item -> {
            StaffMidDept staffMidDept = new StaffMidDept();
            staffMidDept.setDeptId(item).setStaffId(sysStaff.getId());
            staffMidDeptList.add(staffMidDept);
        });
        SpringUtils.getBean(StaffMidDeptService.class).saveBatch(staffMidDeptList);
        return sysUser;
    }

    /**
     * @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.OAM_USER.toString()))
                .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/09/08 15:21
     * @param bo 请求参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    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);
        }
        //再修改员工信息
        Staff sysStaff = BeanUtils.mapToClass(bo, Staff.class);
        this.updateById(sysStaff);
        //批量删除
        StaffMidDeptService staffMidDeptService = SpringUtils.getBean(StaffMidDeptService.class);
        staffMidDeptService.remove(QueryWrapper.create().where(STAFF_MID_DEPT.STAFF_ID.eq(sysStaff.getId())));
        //批量导入部门信息
        List<String> deptList = Arrays.asList(bo.getDeptId().split(","));
        List<StaffMidDept> staffMidDeptList = new ArrayList<>();
        deptList.forEach(item -> {
            StaffMidDept staffMidDept = new StaffMidDept();
            staffMidDept.setDeptId(item).setStaffId(sysStaff.getId());
            staffMidDeptList.add(staffMidDept);
        });
        staffMidDeptService.saveBatch(staffMidDeptList);
        //获取缓存信息
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        JSONObject jsonObject = redisService.getCacheObject(UserType.OAM_USER.getKey() + bo.getUserName());
        if (jsonObject != null) {
            LoginUser loginUser = jsonObject.getObject("user", LoginUser.class);
            OamStaffLoginInfo staffLoginInfo = BeanUtils.mapToClass(bo, OamStaffLoginInfo.class);
            loginUser.setUser(staffLoginInfo);
            redisService.setCacheObject(UserType.OAM_USER.getKey() + loginUser.getUsername(), loginUser);
        }
    }

    /**
     * @descriptions 删除员工
     * @author DB
     * @date 2023/09/08 17:43
     * @param id 主键
     */
    @Override
    public void removeStaffById(String id) {
        //先获取员工
        Staff sysStaff = this.getById(id);
        if (null == sysStaff) {
            throw new ServiceException("获取员工信息失败,请联系相关人员");
        }
        //删除用户
        SpringUtils.getBean(CoreService.class).removeSysUserById(sysStaff.getUserId());
        //删除员工
        this.removeById(id);
    }

    /**
     * @Description: 用户名是否存在
     * @param username 用户名
     * @param id 主键
     * @return: R<Void>
     * @Author: DB
     * @Date: 2023/5/11 10:09
     **/
    @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.ID.ne(id))) > 0) {
            throw new ServiceException(MessageUtils.message("i18n_alert_userIsExist"));
        }
    }

    /**
     * @Description: 获取员工信息
     * @param id 主键
     * @return: StaffInfoVo
     * @Author: DB
     * @Date: 2023/5/17 10:36
     **/
    @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("userId"))
                        .select(ROLE.ROLE_NAME)
                        .from(STAFF)
                        .leftJoin(SYS_USER).on(SYS_USER.ID.eq(STAFF.USER_ID))
                        .leftJoin(ROLE).on(ROLE.ID.eq(STAFF.ROLE_ID))
                        .where(STAFF.ID.eq(id))
                , StaffInfoVo.class);
    }

    /**
     * @Description: 获取用户操作日志分页
     * @param bo 请求参数
     * @return: R<PageVo<SysOperationLogVo>>
     * @Author: DB
     * @Date: 2023/5/12 14:30
     **/
    @Override
    public Page<SysOperationLogVo> getUserLogList(SysUserLogBo bo) {
        QueryWrapper queryWrapper = QueryWrapper.create();
        queryWrapper.and(SYS_OPERATION_LOG.OPERATION_USER_ID.eq(bo.getUserId()))
                .and(SYS_OPERATION_LOG.OPERATION_USER_NAME.like(bo.getOperationUserName()))
                .and(SYS_OPERATION_LOG.DEV_IP.eq(bo.getDevIp()))
                .and(SYS_OPERATION_LOG.RESULT.eq(bo.getResult()))
                .and(SYS_OPERATION_LOG.LEVEL.eq(bo.getLevel()))
                .and(SYS_OPERATION_LOG.ACTION_TYPE.eq(bo.getActionType()));
        if (StringUtils.isNotBlank(bo.getFieldTime())){
            String[] split = bo.getFieldTime().split(",");
            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            queryWrapper.and(SYS_OPERATION_LOG.CREATE_TIME.ge(LocalDateTime.parse(split[0], df)))
                    .and(SYS_OPERATION_LOG.CREATE_TIME.le(LocalDateTime.parse(split[1], df)));
        }
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        Page<Row> page = Db.paginate("cp_sys_operation_log", pageDomain.getPageNum(), pageDomain.getPageSize(), queryWrapper);
        return page.map(row -> row.toEntity(SysOperationLogVo.class));
    }

    /**
     * @Description: 修改系统用户密码
     * @param bo 请求参数
     * @return: AjaxResult<Void>
     * @Author: DB
     * @Date: 2023/5/12 16:00
     **/
    @Override
    public void modifyUserPassword(ModifyUserPasswordBo bo) {
        //只允许超级管理员或自己修改面膜
        LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
        String userName = loginUser.getUsername();
        //同数据库密码进行比较
        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"));
        }
    }

    /**
     * @Description: 查询服务员工列表
     * @return: R<List<OamStaffVo>>
     * @Author: DB
     * @Date: 2023/5/11 17:49
     **/
    @Override
    public List<StaffVo> getServiceStaffList() {
        return SpringUtils.getBean(StaffService.class).listAs(QueryWrapper.create()
                        .select(STAFF.ID, STAFF.NAME, STAFF.USER_ID, STAFF.STAFF_TYPE)
                        .from(STAFF)
                        .leftJoin(SYS_USER).on(SYS_USER.ID.eq(STAFF.USER_ID))
                        .where(STAFF.STAFF_TYPE.eq(1))
                        //不查询停用的员工
                        .and(SYS_USER.STATUS.eq(true)),
                StaffVo.class);
    }

    /**
     * 技术模块-今日事务-查询技术员工列表
     * @author DB
     * @since 2023/12/04
     * @return List<StaffVo>
     */
    @Override
    public List<StaffVo> getTechnologyStaffList() {
        //不查询测试员工
        String testStaffPhone = SpringUtils.getBean(RedisService.class).getCacheObject(OamConfigKey.OAM_CONFIG_KEY + OamConfigEnum.TEST_STAFF_PHONE.getKey());
        return this.listAs(QueryWrapper.create()
                        .select(STAFF.ID, STAFF.NAME, STAFF.USER_ID, STAFF.STAFF_TYPE)
                        .from(STAFF)
                        .leftJoin(SYS_USER).on(SYS_USER.ID.eq(STAFF.USER_ID))
                        .where(STAFF.STAFF_TYPE.eq(0))
                        //不查询停用的员工
                        .and(SYS_USER.STATUS.eq(true))
                        .and(SYS_USER.PHONE_NUMBER.notIn(Arrays.asList(testStaffPhone.split(",")))),
                StaffVo.class);
    }

    /**
     * 修改运维状态
     * @author DB
     * @since 2023/12/07
     * @param bo 请求参数
     */
    @Override
    public void changeOperationStatus(OperationStatusBo bo) {
        //获取用户信息
        Staff staff = this.getById(bo.getId());
        SysUser sysUser = DbChain.table(SYS_USER).select(SYS_USER.PHONE_NUMBER).where(SYS_USER.ID.eq(staff.getUserId())).oneAs(SysUser.class);
        JSONObject jsonBody = new JSONObject();
        if (bo.getIsOperation()) {
            jsonBody.put("_type", "oamAdd");
            jsonBody.put("name", staff.getName());
            jsonBody.put("phone", sysUser.getPhoneNumber());
        } else {
            jsonBody.put("_type", "oamDel");
            jsonBody.put("phone", sysUser.getPhoneNumber());
        }
        JSONObject jsonObject = SpringUtils.getBean(RestTemplate.class).postForObject(JamboxCloudUrl.COMMON_CARD_URL, jsonBody, JSONObject.class);
        assert jsonObject != null;
        if (!jsonObject.getBoolean("success")) {
            throw new ServiceException(jsonObject.getString("error"));
        } else {
            this.updateChain().set(STAFF.IS_OPERATION, bo.getIsOperation()).where(STAFF.ID.eq(bo.getId())).update();
        }
    }

}
