package cd.casic.module.machine.service.impl;

import cd.casic.framework.commons.pojo.PageResult;
import cd.casic.module.machine.controller.vo.SecretKeyVO;
import cd.casic.module.machine.enums.AuthenticationType;
import cd.casic.module.machine.enums.MachineInfoType;
import cd.casic.module.machine.dal.mysql.MachineInfoMapper;
import cd.casic.module.machine.controller.vo.MachineInfoVO;
import cd.casic.module.machine.dal.dataobject.MachineInfoDO;
import cd.casic.module.machine.enums.MachineInfoStatus;
import cd.casic.module.machine.service.MachineInfoService;
import cd.casic.module.machine.service.SecretKeyService;
import cd.casic.module.machine.utils.CryptogramUtil;
import com.google.common.annotations.VisibleForTesting;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import cd.casic.framework.commons.util.object.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import static cd.casic.framework.commons.exception.util.ServiceExceptionUtil.exception;
import static cd.casic.module.machine.contants.MachineErrorCodeConstants.*;

/**
 * 机器信息服务实现类
 */
@Slf4j
@Service("machineInfoToService")
public class MachineInfoServiceImpl implements MachineInfoService {

    @Resource
    private SecretKeyService secretKeyService;

    @Resource
    private MachineInfoMapper machineInfoMapper;

    @Override
    public Long createMachine(MachineInfoVO machineInfoVO) {
        validateMachineEnvAdd(machineInfoVO);
        validateMachineTagUnique(machineInfoVO.getTag());
        MachineInfoDO machineInfoDO = BeanUtils.toBean(machineInfoVO, MachineInfoDO.class);
        if (machineInfoVO.getAuthenticationType() == 2) {
            Long secretKeyId = machineInfoDO.getSecretKeyId();
            SecretKeyVO secretKey = secretKeyService.getSecretKey(secretKeyId);
            if (secretKey == null) {
                throw exception(SECRET_KEY_NOT_EXISTS);
            }
        }
        if (Objects.nonNull(machineInfoDO.getPassword())) {
            machineInfoDO.setPassword(CryptogramUtil.doEncrypt(machineInfoDO.getPassword()));
        }
        machineInfoMapper.insert(machineInfoDO);
        return machineInfoDO.getId();
    }

    @Override
    public void updateMachineInfo(MachineInfoVO machineInfoVO) {
        validateMachineEnvAdd(machineInfoVO);
        String newTag = machineInfoVO.getTag();
        MachineInfoDO machineInfoDO = validateMachineInfoExists(machineInfoVO.getId());
        String oldTag = machineInfoDO.getTag();
        if (!newTag.equals(oldTag)) {
            validateMachineTagUnique(newTag);
        }
        BeanUtils.copyProperties(machineInfoVO, machineInfoDO);
        machineInfoMapper.updateById(machineInfoDO);
    }

    @Override
    public Integer updateStatus(MachineInfoVO machineInfoVO) {
        machineInfoMapper.updateStatus(machineInfoVO.getId(), machineInfoVO.getStatus());
        return machineInfoVO.getStatus();
    }

    @Override
    public PageResult<MachineInfoDO> listMachineInfo(MachineInfoVO machineInfoVO) {
        return machineInfoMapper.selectPage(machineInfoVO);
    }

    @Override
    public void bindingSecretKey(SecretKeyVO secretKeyVO) {
        machineInfoMapper.bindingSecretKey(secretKeyVO.getMachineInfoIds(), secretKeyVO.getId(), secretKeyVO.getEnableBind());
    }

    @Override
    @Transactional//其中一个在线，那么就回滚
    public void deleteMachineInfoList(String machineInfoIds) {
        List<Long> machineInfoIdList = Arrays.stream(machineInfoIds.split(","))
            .map(String::trim)
            .filter(s -> !s.isEmpty())
            .map(Long::parseLong)
            .toList();
        machineInfoIdList.forEach(this::deleteMachineInfo);
    }

    @Override
    public void deleteMachineInfo(Long machineInfoId) {
        MachineInfoDO machineInfoDO = validateMachineInfoExists(machineInfoId);
        validateMachineEnable(machineInfoDO);
        machineInfoMapper.deleteById(machineInfoId);
    }

    @Override
    public List<MachineInfoDO> selectBindMachineBySecretKey(Long secretKeyId) {
        return machineInfoMapper.selectBindMachineBySecretKey(secretKeyId);
    }

    @VisibleForTesting
    void validateMachineEnvAdd(MachineInfoVO machineInfoVO) {
        if (machineInfoVO.getHostIp().isEmpty()) {
            throw exception(MACHINE_INFO_HOST_IP_NULL);
        }
        if (machineInfoVO.getUsername().isEmpty()) {
            throw exception(MACHINE_INFO_USER_NAME_NULL);
        }
        if (machineInfoVO.getTag().isEmpty()) {
            throw exception(MACHINE_INFO_TAG_NULL);
        }

        if (machineInfoVO.getAuthenticationType() != null) {
            boolean flag = true;
            for (int type : AuthenticationType.ARRAYS) {
                if (type == machineInfoVO.getAuthenticationType()) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                throw exception(MACHINE_INFO_AUTHENTICATION_TYPE_NOT_EXISTS);
            }
        } else {
            throw exception(MACHINE_INFO_AUTHENTICATION_TYPE_NULL);
        }


        if (machineInfoVO.getMachineInfoType() != null) {
            boolean flag = true;
            for (int type : MachineInfoType.ARRAYS) {
                if (type == machineInfoVO.getMachineInfoType()) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                throw exception(MACHINE_INFO_TYPE_NOT_EXISTS);
            }
        } else {
            throw exception(MACHINE_INFO_TYPE_NULL);
        }

    }

    @VisibleForTesting
    void validateMachineTagUnique(String tag) {
        if (machineInfoMapper.existsByTag(tag)) {
            throw exception(MACHINE_INFO_TAG_EXISTS);
        }
    }

    @Override
    public MachineInfoDO validateMachineInfoExists(Long id) {
        if (id == null) {
            throw exception(MACHINE_INFO_NULL);
        }
        MachineInfoDO machineInfoDO = machineInfoMapper.selectById(id);
        if (machineInfoDO == null) {
            throw exception(MACHINE_INFO_NULL);
        }
        return machineInfoDO;
    }

    @VisibleForTesting
    void validateMachineEnable(MachineInfoDO machineInfoDO) {
        if (machineInfoDO.getStatus() == MachineInfoStatus.ENABLE.getCode()) {
            throw exception(MACHINE_ENABLE);
        }
    }
}
