package com.mini.capi.sys.service;

import com.mini.capi.biz.domain.DockerContainerInfo;
import com.mini.capi.biz.domain.DockerHost;
import com.mini.capi.biz.domain.SshInfo;
import com.mini.capi.biz.domain.SshUser;
import com.mini.capi.biz.service.DockerContainerInfoService;
import com.mini.capi.biz.service.DockerHostService;
import com.mini.capi.biz.service.SshInfoService;
import com.mini.capi.biz.service.SshUserService;
import com.mini.capi.model.ApiResult;
import com.mini.capi.utils.HostRuntime;
import com.mini.capi.utils.docker;
import com.mini.capi.utils.vDate;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

@Service
public class HostService {


    @Resource
    private SshInfoService sshInfoService;

    @Resource
    private SshUserService sshUserService;

    @Resource
    private DockerHostService dockerHostService;
    @Resource
    private DockerContainerInfoService dockerInfoService;


    @Resource
    private Executor hostExecutor;


    public static class SnapshotDTO {
        public String hostName;
        public String timestamp;
        public String cpuUsage;
        public String memTotal;
        public String memFree;
        public String memUsed;
        public String memUsage;
        public String swapTotal;
        public String swapUsed;
        public List<SnapshotDTO.DiskDTO> disks;
        public String netRxBytes;
        public String netTxBytes;
        public double load1;
        public int processCount;
        public String uptimeSec;

        public static SnapshotDTO from(HostRuntime.Snapshot s) {
            SnapshotDTO dto = new SnapshotDTO();
            dto.hostName = s.hostName;
            dto.timestamp = Instant.ofEpochMilli(s.timestamp)
                    .atZone(ZoneId.systemDefault())
                    .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            dto.cpuUsage = String.format("%.2f %%", s.cpuUsage * 100);
            dto.memTotal = humanBytes(s.memTotal);
            dto.memFree = humanBytes(s.memFree);
            dto.memUsed = humanBytes(s.memUsed);
            dto.memUsage = String.format("%.2f %%", s.memUsage * 100);
            dto.swapTotal = humanBytes(s.swapTotal);
            dto.swapUsed = humanBytes(s.swapUsed);
            dto.disks = s.disks.stream()
                    .map(d -> new SnapshotDTO.DiskDTO(d.path,
                            humanBytes(d.total),
                            humanBytes(d.free),
                            humanBytes(d.used),
                            String.format("%.2f %%", d.usage * 100)))
                    .collect(Collectors.toList());
            dto.netRxBytes = humanBytes(s.netRxBytes);
            dto.netTxBytes = humanBytes(s.netTxBytes);
            dto.load1 = s.load1;
            dto.processCount = s.processCount;
            dto.uptimeSec = uptimeToHuman(s.uptimeSec);
            return dto;
        }

        private static String humanBytes(long bytes) {
            if (bytes < 1024) return bytes + " B";
            int exp = (int) (Math.log(bytes) / Math.log(1024));
            String pre = "KMGTPE".charAt(exp - 1) + "iB";
            return String.format("%.1f %s", bytes / Math.pow(1024, exp), pre);
        }

        private static String uptimeToHuman(long sec) {
            long h = sec / 3600;
            long m = (sec % 3600) / 60;
            long s = sec % 60;
            if (h > 0) return String.format("%d 时 %d 分 %d 秒", h, m, s);
            if (m > 0) return String.format("%d 分 %d 秒", m, s);
            return String.format("%d 秒", s);
        }

        public static class DiskDTO {
            public String path;
            public String total;
            public String free;
            public String used;
            public String usage;

            public DiskDTO(String path, String total, String free, String used, String usage) {
                this.path = path;
                this.total = total;
                this.free = free;
                this.used = used;
                this.usage = usage;
            }
        }
    }


    private static final int MAX_SIZE = 100;


    private String buildKey(String dockerHostId, String image, String containerName) {
        return dockerHostId + "|" + image + "|" + containerName;
    }

    private void addOrEdit(String dockerHostId, Map<String, DockerContainerInfo> oldMap, SshInfo sshInfo, List<docker.DockerInfo> remoteList) {
        for (docker.DockerInfo d : remoteList) {
            String key = buildKey(dockerHostId, d.getImageName(), d.getNames());
            DockerContainerInfo entity = oldMap.get(key);
            if (entity == null) {
                entity = new DockerContainerInfo();
                entity.setDokerHostId(dockerHostId);
                entity.setContainerId(d.getContainerId());
                entity.setImageName(d.getImageName());
                entity.setCommand(d.getCommand());
                entity.setCreatedAt(d.getCreatedAt());
                entity.setUnames(d.getNames());
                entity.setUstatus(d.getStatus());
                entity.setHostIp(sshInfo.getHostIp());
                entity.setPorts(d.getPorts());
                entity.setGetTime(vDate.getNow());
                entity.setDokerHostId(dockerHostId);
                entity.setFTenantId("0");
                dockerInfoService.save(entity);
            } else {
                entity.setContainerId(d.getContainerId());
                entity.setImageName(d.getImageName());
                entity.setCommand(d.getCommand());
                entity.setCreatedAt(d.getCreatedAt());
                entity.setUnames(d.getNames());
                entity.setUstatus(d.getStatus());
                entity.setHostIp(sshInfo.getHostIp());
                entity.setPorts(d.getPorts());
                entity.setDokerHostId(dockerHostId);
                entity.setUpdateTime(vDate.getNow());
                dockerInfoService.updateById(entity);
                oldMap.remove(key);
            }
        }
    }

    private void refreshContainerTable(String dockerHostId,
                                       List<docker.DockerInfo> remoteList,
                                       SshInfo sshInfo) {

        /* 1. 旧数据 */
        List<DockerContainerInfo> oldList = dockerInfoService.lambdaQuery()
                .eq(DockerContainerInfo::getDokerHostId, dockerHostId)
                .list();
        Map<String, DockerContainerInfo> oldMap = oldList.stream()
                .collect(Collectors.toMap(
                        c -> buildKey(dockerHostId, c.getImageName(), c.getUnames()),
                        c -> c));

        /* 2. 遍历远端 */
        addOrEdit(dockerHostId, oldMap, sshInfo, remoteList);

        /* 3. 无效删除 */
        if (!oldMap.isEmpty()) {
            List<String> ids = new ArrayList<>();
            for (DockerContainerInfo e : oldMap.values()) {
                ids.add(e.getId());
            }
            dockerInfoService.removeByIds(ids);
        }
    }

    private void updateRunNum(DockerHost host, List<docker.DockerInfo> list) {
        long runningCnt = list.stream()
                .filter(r -> "1".equals(r.getStatus()))
                .count();
        host.setRunNum(runningCnt);
        host.setUpdateTime(vDate.getNow());
        dockerHostService.updateById(host);
    }


    private void handleSingleDockerHost(DockerHost host, List<String> errorList) {
        try {
            /* 1. 一次性加载现有数据 */
            List<DockerContainerInfo> oldList =
                    dockerInfoService.lambdaQuery()
                            .eq(DockerContainerInfo::getDokerHostId, host.getDokerHostId())
                            .list();

            Map<String, DockerContainerInfo> oldMap = oldList.stream()
                    .collect(Collectors.toMap(
                            c -> buildKey(c.getDokerHostId(), c.getImageName(), c.getUnames()),
                            c -> c));

            /* 2. 远程最新数据 */
            SshUser sshUser = sshUserService.getById(host.getUserId());
            SshInfo sshInfo = sshInfoService.getById(host.getHostId());
            List<docker.DockerInfo> remoteList =
                    docker.getDockerInfo(sshInfo.getHostIp(),
                            Long.valueOf(sshInfo.getHostPort()),
                            sshUser.getCUsername(),
                            sshUser.getCPassword());

            /* 3. 新增或更新 */
            addOrEdit(host.getDokerHostId(), oldMap, sshInfo, remoteList);

            /* 4. 删除已失效容器 */
            if (!oldMap.isEmpty()) {
                List<String> idsToDel = oldMap.values()
                        .stream()
                        .map(DockerContainerInfo::getId)
                        .collect(Collectors.toList());
                dockerInfoService.removeByIds(idsToDel);
            }

            /* 5. 更新运行数 */
            long runningCnt = remoteList.stream()
                    .filter(r -> "1".equals(r.getStatus()))
                    .count();
            host.setRunNum(runningCnt);
            host.setUpdateTime(vDate.getNow());
            dockerHostService.updateById(host);
        } catch (Exception e) {
            errorList.add(String.format("hostId=%s, error=%s",
                    host.getDokerHostId(), e.getMessage()));
        }
    }


    public ApiResult<List<SnapshotDTO>> getApiInfo() {
        try {
            // 1. 新建一个一次性 List
            List<HostRuntime.Snapshot> snapshots =
                    Collections.synchronizedList(new LinkedList<>());
            // 2. 采集并加入
            HostRuntime.Snapshot snap = HostRuntime.collect();
            snapshots.add(snap);
            // 3. 如果只想保留一条，直接清空多余
            while (snapshots.size() > MAX_SIZE) {
                ((LinkedList<HostRuntime.Snapshot>) snapshots).removeFirst();
            }
            return ApiResult.success(Collections.singletonList(SnapshotDTO.from(snap)));
        } catch (Exception e) {
            return ApiResult.error(101, e.getMessage());
        }
    }


    /**
     * 获取容器列表
     */
    public ApiResult<?> getDockerInfo() {
        try {
            List<DockerHost> dockerHosts = dockerHostService.list();
            List<String> errorList = Collections.synchronizedList(new ArrayList<>());
            CompletableFuture<?>[] futures = dockerHosts.stream()
                    .map(host -> CompletableFuture.runAsync(
                            () -> handleSingleDockerHost(host, errorList), hostExecutor))
                    .toArray(CompletableFuture[]::new);
            // 等待全部执行完毕
            CompletableFuture.allOf(futures).join();
            return errorList.isEmpty()
                    ? ApiResult.success()
                    : ApiResult.error();
        } catch (Exception e) {
            return ApiResult.error(101, e.getMessage());
        }
    }


    /**
     * 启动容器
     */
    public ApiResult<?> startDockerInfo(String id) {
        try {
            DockerContainerInfo cur = dockerInfoService.getById(id);
            DockerHost host = dockerHostService.getById(cur.getDokerHostId());
            SshUser sshUser = sshUserService.getById(host.getUserId());
            SshInfo sshInfo = sshInfoService.getById(host.getHostId());
            docker.startDocker(sshInfo.getHostIp(), Long.valueOf(sshInfo.getHostPort()), sshUser.getCUsername(), sshUser.getCPassword(), cur.getContainerId());
            /* 2. 取回最新列表 */
            List<docker.DockerInfo> remoteList = docker.getDockerInfo(
                    sshInfo.getHostIp(),
                    Long.valueOf(sshInfo.getHostPort()),
                    sshUser.getCUsername(),
                    sshUser.getCPassword());
            /* 3. 有则更新、无则插入、失效删除 */
            refreshContainerTable(cur.getDokerHostId(), remoteList, sshInfo);
            /* 4. 更新主机运行数 */
            updateRunNum(host, remoteList);
            return ApiResult.success();
        } catch (Exception e) {
            return ApiResult.error(101, e.getMessage());
        }
    }


    /**
     * 停止容器
     */
    public ApiResult<?> stopDockerInfo(String id) {
        try {
            DockerContainerInfo cur = dockerInfoService.getById(id);
            DockerHost host = dockerHostService.getById(cur.getDokerHostId());
            SshUser sshUser = sshUserService.getById(host.getUserId());
            SshInfo sshInfo = sshInfoService.getById(host.getHostId());
            docker.stopDocker(sshInfo.getHostIp(), Long.valueOf(sshInfo.getHostPort()), sshUser.getCUsername(), sshUser.getCPassword(), cur.getContainerId());
            /* 2. 取回最新列表 */
            List<docker.DockerInfo> remoteList = docker.getDockerInfo(
                    sshInfo.getHostIp(),
                    Long.valueOf(sshInfo.getHostPort()),
                    sshUser.getCUsername(),
                    sshUser.getCPassword());
            /* 3. 有则更新、无则插入、失效删除 */
            refreshContainerTable(cur.getDokerHostId(), remoteList, sshInfo);
            /* 4. 更新主机运行数 */
            updateRunNum(host, remoteList);
            return ApiResult.success();
        } catch (Exception e) {
            return ApiResult.error(101, e.getMessage());
        }
    }
}
