package cn.iocoder.yudao.module.system.service.user;

import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.controller.admin.equipment.vo.userExt.UsersExtSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.equipment.vo.websocket.AddUserVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.factoryUser.FactoryUserSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.equipment.AttendanceMachineDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.equipment.AttendanceMachineService;
import cn.iocoder.yudao.module.system.service.equipment.UsersExtService;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import cn.iocoder.yudao.module.system.service.websocket.WebsocketService;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.util.List;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 后台工厂用户 Service 实现类
 */
@Service
@Slf4j
public class FactoryUserServiceImpl implements FactoryUserService{

    @Resource
    @Lazy // 延迟，避免循环依赖报错
    private TenantService tenantService;

    @Resource
    private AdminUserMapper userMapper;
    @Resource
    private AdminUserService adminUserService;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    @Lazy
    private UsersExtService usersExtService;

    @Resource
    @Lazy
    private DeptService deptService;

    @Resource
    private AttendanceMachineService attendanceMachineService;

    @Resource
    private WebsocketService websocketService;

    @Override
//    @Transactional(rollbackFor = Exception.class)  // 异常回滚
    public String createUser(FactoryUserSaveReqVO createReqVO){

        // 校验账户配合
        tenantService.handleTenantInfo(tenant -> {
            long count = userMapper.selectCount();
            if (count >= tenant.getAccountCount()) {
                throw exception(USER_COUNT_MAX, tenant.getAccountCount());
            }
        });

        // 校验手机号唯一
        validateMobileUnique(createReqVO.getMobile());

        // 根据工厂ID 获取 对应部门ID
        DeptDO deptDO = deptService.getDeptByFactoryId(createReqVO.getFactoryId());
        if (deptDO == null) {
            throw exception(FACTORY_NOT_DEPT);
        }

        // 插入用户
        AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class);
        user.setDeptId(deptDO.getId());
        user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
        user.setPassword(encodePassword());  // 设置默认密码
        user.setUserType(2);  // 设置用户类型为 工厂用户
        user.setUserStaffing(7);
        userMapper.insert(user);

        // 查询部门下是否绑定考勤设备
        List<AttendanceMachineDO> attendanceMachineDOS = attendanceMachineService.getListByDeptId(deptDO.getId());

        // 插入人脸图片
        UsersExtSaveReqVO usersExtDO = BeanUtils.toBean(createReqVO, UsersExtSaveReqVO.class);
        usersExtDO.setUserId(user.getId());
        usersExtDO.setDeptId(deptDO.getId());
        usersExtDO.setAttendanceMachineNos(convertList(attendanceMachineDOS, AttendanceMachineDO::getDeviceNo));
//        usersExtService.createUsers(usersExtDO);
//        // 工厂用户添加到GPS定位系统中
//        adminUserService.gpsIncrementalSynchronizationOfUsers(user);
        try {

            for (AttendanceMachineDO machineDO : attendanceMachineDOS) {

                // 设置下发命令
                AddUserVO addUserVO = new AddUserVO()
                        .setCmd("addUser")
                        .setUser_id(String.valueOf(user.getId()))
                        .setName(createReqVO.getNickname())
                        .setFace_template(usersExtDO.getFaceImg())
                        .setId_valid("");

                // 发送指令至 考勤设备 下发员工
                websocketService.sendSn(machineDO.getDeviceNo(), addUserVO, 1);

                // 下发成功则设置 用户绑定设备
                usersExtDO.setAttendanceMachineNos(convertList(attendanceMachineDOS, AttendanceMachineDO::getDeviceNo));
            }
        } catch (Exception ex) {

            // 手动回滚
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

            // 下发失败则清空 用户绑定设备
            usersExtDO.setAttendanceMachineNos(null);
            return ex.getMessage();
        }finally {

            usersExtService.createUsers(usersExtDO);
        }

        return null;
    }

    @VisibleForTesting
    void validateMobileUnique(String mobile) {
        if (StrUtil.isBlank(mobile)) {
            return;
        }
        AdminUserDO user = userMapper.selectByMobile(mobile);
        if (user == null) {
            return;
        }

        if (!user.getMobile().equals(mobile)) {
            throw exception(USER_MOBILE_EXISTS);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)  // 异常回滚
    public String updateUser(FactoryUserSaveReqVO reqVO) {

        // 校验是否存在
        validateId(reqVO.getId());

        // 根据工厂ID 获取 对应部门ID
        DeptDO deptDO = deptService.getDeptByFactoryId(reqVO.getFactoryId());
        if (deptDO == null) {
            throw exception(FACTORY_NOT_DEPT);
        }

        // 更新用户信息
        AdminUserDO userDO = BeanUtils.toBean(reqVO, AdminUserDO.class);
        userDO.setDeptId(deptDO.getId());
        userMapper.updateById(userDO);

        // 查询部门下是否绑定考勤设备
        List<AttendanceMachineDO> attendanceMachineDOS = attendanceMachineService.getListByDeptId(deptDO.getId());

        // 同步更新用户拓展表中 人脸信息
        UsersExtSaveReqVO usersExt = new UsersExtSaveReqVO()
                .setUserId(reqVO.getId())
                .setDeptId(deptDO.getId())
                .setAttendanceMachineNos(convertList(attendanceMachineDOS, AttendanceMachineDO::getDeviceNo))
                .setWorkType(reqVO.getWorkType())
                .setFaceImg(reqVO.getFaceImg());
        usersExtService.updateUser(usersExt);

        try {

            // 如果修改人脸图片的情况
            for (AttendanceMachineDO machineDO : attendanceMachineDOS) {

                // 设置下发命令
                AddUserVO addUserVO = new AddUserVO()
                        .setCmd("addUser")
                        .setUser_id(String.valueOf(reqVO.getId()))
                        .setName(reqVO.getNickname())
                        .setFace_template(reqVO.getFaceImg())
                        .setId_valid("");

                // 发送指令至 考勤设备 修改员工信息
                websocketService.sendSn(machineDO.getDeviceNo(), addUserVO, 1);
            }
        }catch (Exception ex) {

            // 手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ex.getMessage();
        }

        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)  // 异常回滚
    public String deleteUser(Long id) {

        // 校验是否存在
        validateId(id);

        // 删除用户信息
        userMapper.deleteById(id);

        // 同步删除用户拓展信息
        try {

            usersExtService.deleteUser(id);
        } catch (Exception ex) {

            // 手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ex.getMessage();
        }
        return null;
    }

    private void validateId(Long id) {

        if (userMapper.selectCount(AdminUserDO::getId, id) == 0L) {
            throw exception(USER_USERNAME_EXISTS);
        }
    }

    /**
     * 对密码进行加密
     *
     * @return 加密后的密码
     */
    private String encodePassword() {
        return passwordEncoder.encode("123456");
    }
}
