package cn.reghao.bnt.web.devops.machine.service.impl;

import cn.reghao.bnt.common.msg.constant.NodeStatus;
import cn.reghao.bnt.web.util.SelectOption;
import cn.reghao.bnt.web.devops.app.db.repository.config.AppDeployConfigRepository;
import cn.reghao.bnt.web.devops.machine.db.repository.MachineHostRepository;
import cn.reghao.bnt.web.devops.machine.db.repository.MachineInfoRepository;
import cn.reghao.bnt.web.devops.machine.model.po.MachineHost;
import cn.reghao.bnt.web.devops.machine.model.po.MachineInfo;
import cn.reghao.bnt.web.devops.machine.model.vo.MachineDetail;
import cn.reghao.bnt.web.devops.machine.model.vo.MachineStat;
import cn.reghao.bnt.web.devops.machine.service.MachineQuery;
import cn.reghao.jutil.jdk.converter.DateTimeConverter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2024-08-04 19:59:20
 */
@Service
public class MachineQueryImpl implements MachineQuery {
    private final MachineInfoRepository machineInfoRepository;
    private final MachineHostRepository machineHostRepository;
    // TODO MachineQuery 和 AppDeployQuery 存在循环依赖
    private final AppDeployConfigRepository deployConfigRepository;

    public MachineQueryImpl(MachineInfoRepository machineInfoRepository, MachineHostRepository machineHostRepository,
                            AppDeployConfigRepository deployConfigRepository) {
        this.machineInfoRepository = machineInfoRepository;
        this.machineHostRepository = machineHostRepository;
        this.deployConfigRepository = deployConfigRepository;
    }

    @Override
    public Page<MachineDetail> getMachineHostByPage(String env, Pageable pageable) {
        Page<MachineHost> page = machineHostRepository.findByEnv(env, pageable);
        return page.map(machineHost -> {
            int used = deployConfigRepository.countByMachineHost(machineHost);
            MachineInfo machineInfo = machineHost.getMachineInfo();
            long bootTime = machineInfo.getBootTime();
            String bootTimeStr = DateTimeConverter.format(bootTime*1000);
            String status = getMachineStat(machineInfo);
            return new MachineDetail(machineHost, bootTimeStr, status, used);
        });
    }

    @Override
    public List<SelectOption> getMachineApps(String machineId) {
        MachineHost machineHost = machineHostRepository.findByMachineId(machineId);
        if (machineHost == null) {
            return Collections.emptyList();
        }

        return deployConfigRepository.findByMachineHost(machineHost).stream()
                .map(appDeployConfig -> {
                    String appId = appDeployConfig.getAppConfig().getAppId();
                    String appName = appDeployConfig.getAppConfig().getAppName();
                    return new SelectOption(appId, appName);
                })
                .collect(Collectors.toList());
    }

    @Override
    public List<MachineStat> getMachineStats() {
        PageRequest pageRequest = PageRequest.of(0, 100);
        Page<MachineHost> page = machineHostRepository.findAll(pageRequest);
        Map<String, List<MachineHost>> map = page.stream().collect(Collectors.groupingBy(MachineHost::getEnv));

        List<MachineStat> stats = new ArrayList<>();
        map.forEach((env, list) -> {
            int total = list.size();
            int onlineCount = (int)list.stream()
                    .filter(machineHost -> machineHost.getMachineInfo().getStat() == NodeStatus.Online.getCode())
                    .count();
            int offlineCount = (int) list.stream()
                    .filter(machineHost -> machineHost.getMachineInfo().getStat() != NodeStatus.Online.getCode())
                    .count();
            MachineStat machineStat = new MachineStat(env, total, onlineCount, offlineCount);
            stats.add(machineStat);
        });

        return stats;
    }

    private String getMachineStat(MachineInfo machineInfo) {
        String status = NodeStatus.Online.name();
        int stat = machineInfo.getStat();
        if (stat == NodeStatus.Offline.getCode()) {
            status = NodeStatus.Offline.name();
        } else if (stat == NodeStatus.Deprecated.getCode()) {
            status = NodeStatus.Deprecated.name();
        }

        return status;
    }

    @Override
    public MachineHost getMachineHost(String machineId) {
        return machineHostRepository.findByMachineId(machineId);
    }

    @Override
    public List<MachineInfo> getMachineInfos(List<String> machineIds) {
        if (machineIds.isEmpty()) {
            return Collections.emptyList();
        }

        Specification<MachineInfo> specification = (root, query, cb) -> {
            List<Predicate> list = new ArrayList<>();
            Expression<String> exp = root.get("machineId");
            // SQL IN 查询
            list.add(exp.in(machineIds));
            return cb.and(list.toArray(new Predicate[0]));
        };

        return machineInfoRepository.findAll(specification);
    }

    //@Override
    public List<MachineInfo> getMachineInfos(int stat) {
        List<MachineInfo> list = machineInfoRepository.findByStat(NodeStatus.Online.getCode());
        return list;
    }

    @Override
    public List<SelectOption> getOnlineMachines(String env) {
        PageRequest pageRequest = PageRequest.of(0, 100);
        return machineHostRepository.findByEnv(env, pageRequest).stream()
                .filter(machineHost -> machineHost.getMachineInfo().getStat().equals(NodeStatus.Online.getCode()))
                .map(machineHost -> new SelectOption(machineHost.getMachineId(), machineHost.getMachineIpv4()))
                .collect(Collectors.toList());
    }

    @Override
    public boolean isAgentOnline(String machineId) {
        MachineInfo machineInfo = machineInfoRepository.findByMachineId(machineId);
        if (machineInfo == null) {
            return false;
        }

        return Objects.equals(machineInfo.getStat(), NodeStatus.Online.getCode());
    }
}
