package cn.reghao.devops.mgr.machine.service.impl;

import cn.reghao.devops.common.msg.constant.NodeStatus;
import cn.reghao.devops.common.msg.event.EvtAgentHeartbeat;
import cn.reghao.devops.common.msg.event.EvtAgentStart;
import cn.reghao.devops.mgr.machine.db.query.MachineQuery;
import cn.reghao.devops.mgr.machine.db.repository.MachineHostRepository;
import cn.reghao.devops.mgr.machine.db.repository.MachineInfoRepository;
import cn.reghao.devops.mgr.machine.model.po.MachineHost;
import cn.reghao.devops.mgr.machine.model.po.MachineInfo;
import cn.reghao.devops.mgr.machine.model.po.SshAuth;
import cn.reghao.devops.mgr.machine.service.MachineService;
import cn.reghao.jutil.jdk.machine.data.detail.DiskDetail;
import cn.reghao.jutil.jdk.machine.data.detail.MemoryDetail;
import cn.reghao.jutil.jdk.result.Result;
import cn.reghao.jutil.jdk.result.ResultStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2019-11-15 08:48:04
 */
@Slf4j
@Service
public class MachineServiceImpl implements MachineService {
    private final MachineHostRepository machineHostRepository;
    private final MachineInfoRepository machineInfoRepository;
    private final MachineQuery machineQuery;

    public MachineServiceImpl(MachineHostRepository machineHostRepository, MachineInfoRepository machineInfoRepository,
                              MachineQuery machineQuery) {
        this.machineHostRepository = machineHostRepository;
        this.machineInfoRepository = machineInfoRepository;
        this.machineQuery = machineQuery;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addOrUpdateMachine(EvtAgentStart evtAgentStart) {
        String machineId = evtAgentStart.getMachineId();
        long timestamp = evtAgentStart.getTimestamp();

        MachineInfo machineInfo = new MachineInfo(evtAgentStart);
        MachineInfo infoEntity = machineQuery.getMachineInfo(machineId);
        if (infoEntity == null) {
            infoEntity = machineInfoRepository.save(machineInfo);
            machineHostRepository.save(new MachineHost(infoEntity));
        } else {
            machineInfo.setId(infoEntity.getId());
            machineInfo.setCreateTime(infoEntity.getCreateTime());
            machineInfoRepository.save(machineInfo);

            MachineHost hostEntity = machineQuery.getMachineHost(machineId);
            String machineIpv4 = infoEntity.getMachineIpv4();
            if (!hostEntity.getMachineIpv4().equals(machineIpv4)) {
                hostEntity.setMachineIpv4(machineIpv4);
                machineHostRepository.save(hostEntity);
            }
        }
    }

    @Override
    public void updateMachine(EvtAgentHeartbeat evtAgentHeartbeat) {
        // TODO 根据 EvtAgentHeartbeat 中的信息检查系统负载，CPU，内存，磁盘等使用情况(暂未实现)
        String machineId = evtAgentHeartbeat.getMachineId();
        long timestamp = evtAgentHeartbeat.getTimestamp();

        MemoryDetail memoryDetail = evtAgentHeartbeat.getMemoryDetail();
        List<DiskDetail> diskDetails = evtAgentHeartbeat.getDiskDetails();
        MachineInfo machineInfo = machineQuery.getMachineInfo(machineId);
        if (machineInfo != null) {
            String machineIpv4 = machineInfo.getMachineIpv4();
            //log.info("heartbeat from {}", machineIpv4);
        } else {
            //log.info("heartbeat from {}", machineId);
        }
    }

    @Override
    public void setMachineEnv(String machineId, String env) {
        MachineHost machineHost = machineQuery.getMachineHost(machineId);
        machineHost.setEnv(env);
        machineHostRepository.save(machineHost);
    }

    @Override
    public void setMachineSshAuth(String machineId, SshAuth sshAuth) {
        // TODO 检测主机可达, 端口可达, 协议正确
        String ipv4 = sshAuth.getIpv4();
        int port = sshAuth.getPort();

        MachineHost machineHost = machineQuery.getMachineHost(machineId);
        machineHost.setSshAuth(sshAuth);
        machineHostRepository.save(machineHost);
    }

    @Override
    public Result setMachinesDeprecate(List<String> machineIds) {
        List<MachineInfo> list = machineQuery.getMachineInfos(machineIds);
        // TODO 这里并没有调用 save, 但仍会持久化
        List<MachineInfo> list1 = list.stream()
                .filter(machineInfo -> !Objects.equals(machineInfo.getStat(), NodeStatus.Online.getCode()))
                .peek(machineInfo -> machineInfo.setStat(NodeStatus.Deprecated.getCode()))
                .collect(Collectors.toList());

        if (!list1.isEmpty()) {
            machineInfoRepository.saveAll(list1);
        }

        return Result.result(ResultStatus.SUCCESS);
    }

    @Transactional(rollbackFor = Exception.class)
    public Result deleteMachine(String machineId) {
        MachineHost machineHost = machineQuery.getMachineHost(machineId);
        if (machineHost == null) {
            return Result.result(ResultStatus.SUCCESS);
        }

        MachineInfo machineInfo = machineHost.getMachineInfo();
        if (!machineInfo.getStat().equals(NodeStatus.Deprecated.getCode())) {
            return Result.result(ResultStatus.FAIL, "只能删除 Deprecated 状态的机器");
        }

        machineHostRepository.deleteByMachineId(machineId);
        machineInfoRepository.deleteByMachineId(machineId);
        return Result.result(ResultStatus.SUCCESS);
    }

    @Override
    public void setAgentStatus(String machineId, NodeStatus nodeStatus) {
        MachineInfo machineInfo = machineQuery.getMachineInfo(machineId);
        if (machineInfo != null) {
            machineInfo.setStat(nodeStatus.getCode());
            machineInfoRepository.save(machineInfo);
        }
    }

    /**
     * mgr 进程结束时将所有 Online 状态的 agent 节点设置为 Offline
     *
     * @param
     * @return
     * @date 2025-06-05 11:34:21
     */
    @Override
    public void shutdown() {
        List<MachineInfo> list = machineQuery.getMachineInfos(NodeStatus.Online.getCode()).stream()
                .filter(machineInfo -> !machineInfo.getMachineId().startsWith("oss"))
                .peek(machineInfo -> machineInfo.setStat(NodeStatus.Offline.getCode()))
                .collect(Collectors.toList());
        machineInfoRepository.saveAll(list);
    }
}
