package cn.reghao.tnb.file.app.zoss.service;

import cn.reghao.jutil.jdk.converter.ByteConverter;
import cn.reghao.jutil.jdk.converter.ByteType;
import cn.reghao.jutil.jdk.math.Calculator;
import cn.reghao.jutil.jdk.web.result.Result;
import cn.reghao.tnb.oss.api.dto.StoreNodeDto;
import cn.reghao.tnb.oss.api.dto.disk.DiskVolume;
import cn.reghao.tnb.file.app.zoss.db.mapper.StoreNodeMapper;
import cn.reghao.tnb.file.app.zoss.db.mapper.StoreVolumeMapper;
import cn.reghao.tnb.file.app.zoss.db.mapper.UserNodeMapper;
import cn.reghao.tnb.file.app.zoss.db.repository.StoreRepository;
import cn.reghao.tnb.file.app.zoss.model.po.StoreNode;
import cn.reghao.tnb.file.app.zoss.model.po.StoreVolume;
import cn.reghao.tnb.common.db.SelectOption;
import cn.reghao.tnb.file.app.zoss.model.vo.StoreNodeInfo;
import cn.reghao.tnb.file.app.zoss.rpc.StoreServiceWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

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

/**
 * @author reghao
 * @date 2024-02-23 13:01:38
 */
@Slf4j
@Service
public class StoreNodeService {
    private final ByteConverter byteConverter;
    private final StoreNodeMapper storeNodeMapper;
    private final StoreVolumeMapper storeVolumeMapper;
    private final UserNodeMapper userNodeMapper;
    private final StoreServiceWrapper storeServiceWrapper;
    private final StoreRepository storeRepository;
    private final UserNodeService userNodeService;
    private final UploadChannelService uploadChannelService;

    public StoreNodeService(ByteConverter byteConverter, StoreNodeMapper storeNodeMapper,
                            StoreVolumeMapper storeVolumeMapper, UserNodeMapper userNodeMapper,
                            StoreServiceWrapper storeServiceWrapper, StoreRepository storeRepository,
                            UserNodeService userNodeService, UploadChannelService uploadChannelService) {
        this.byteConverter = byteConverter;
        this.storeNodeMapper = storeNodeMapper;
        this.storeVolumeMapper = storeVolumeMapper;
        this.userNodeMapper = userNodeMapper;
        this.storeServiceWrapper = storeServiceWrapper;
        this.storeRepository = storeRepository;
        this.userNodeService = userNodeService;
        this.uploadChannelService = uploadChannelService;
    }

    public void addOrUpdate(StoreNodeDto storeNodeDto) {
        String nodeAddr = storeNodeDto.getNodeAddr();
        int httpPort = storeNodeDto.getHttpPort();
        StoreNode storeNode = storeNodeMapper.findByNodeAddrAndHttpPort(nodeAddr, httpPort);
        if (storeNode == null) {
            storeRepository.saveStoreNode(storeNodeDto);
            init();
        } else {
            int storeNodeId = storeNode.getId();
            List<StoreVolume> storeVolumes = storeNodeDto.getDiskVolumes().stream()
                    .map(diskVolume -> new StoreVolume(storeNodeId, diskVolume))
                    .collect(Collectors.toList());
            //storeVolumeMapper.saveAll(storeVolumes);
        }
    }

    private void init() {
        // 没有 UserNode
        int total = userNodeMapper.findAll().size();
        if (total == 0) {
            userNodeService.initUserNode();
        }
        try {
            uploadChannelService.initUploadChannel();
        } catch (Exception e) {
            log.error("初始化 UploadChannel 异常: {}", e.getMessage());
        }
    }

    public Result updateStatus(int storeNodeId) {
        StoreNode storeNode = storeNodeMapper.findById(storeNodeId);
        if (storeNode != null) {
            boolean enabled = storeNode.getEnabled();
            storeNode.setEnabled(!enabled);
            storeNodeMapper.save(storeNode);
            return Result.success();
        }

        return Result.fail("存储节点不存在");
    }

    public Result delete(int storeNodeId) {
        StoreNode storeNode = storeNodeMapper.findById(storeNodeId);
        if (storeNode == null) {
            return Result.fail("node not exist");
        }

        int total = userNodeMapper.countByStoreNodeId(storeNodeId);
        if (total > 0) {
            return Result.fail("someone uses the node");
        }

        storeNodeMapper.delete(storeNode);
        return Result.success();
    }

    public List<StoreNodeInfo> getByPage() {
        List<StoreNode> page = storeNodeMapper.findAll();
        List<StoreNodeInfo> list =  page.stream().map(storeNode -> {
            List<StoreVolume> storeVolumeList = storeVolumeMapper.findByStoreNodeId(storeNode.getId());
            long total = 0L;
            long avail = 0L;
            for (StoreVolume storeVolume : storeVolumeList) {
                total += storeVolume.getTotalSpace();
                avail += storeVolume.getAvailSpace();
            }
            long used = total - avail;

            String totalStr = byteConverter.convert(ByteType.Bytes, total);
            String usedStr = byteConverter.convert(ByteType.Bytes, used);
            double percent = 0;
            if (avail != 0) {
                percent = Calculator.getPercentage(total, avail);
            }
            return new StoreNodeInfo(storeNode, totalStr, usedStr, percent);
        }).collect(Collectors.toList());

        return list;
    }

    public StoreNode getByStoreNodeId(int id) {
        return storeNodeMapper.findById(id);
    }

    public List<SelectOption> getNodeKeyValues() {
        return storeNodeMapper.findAll().stream()
                .map(storeNode -> {
                    int storeNodeId = storeNode.getId();
                    String nodeAddr = storeNode.getNodeAddr();
                    return new SelectOption(nodeAddr, storeNodeId+"");
                }).collect(Collectors.toList());
    }

    public List<StoreVolume> getStoreDisks(int storeNodeId) {
        StoreNode storeNode = storeNodeMapper.findById(storeNodeId);
        if (storeNode == null) {
            return null;
        }

        List<DiskVolume> list = storeServiceWrapper.getDiskVolumes(storeNodeId);
        List<StoreVolume> storeVolumes = list.stream()
                .map(diskVolume -> new StoreVolume(storeNodeId, diskVolume))
                .collect(Collectors.toList());
        //storeVolumeMapper.saveAll(storeVolumes);

        storeVolumes.forEach(storeVolume -> {
            long total = storeVolume.getTotalSpace();
            long avail = storeVolume.getAvailSpace();
            long used = total - avail;
            double percent = Calculator.getPercentage(total, avail);
            String totalStr = byteConverter.convert(ByteType.Bytes, total);
            String usedStr = byteConverter.convert(ByteType.Bytes, used);
            storeVolume.setUsedSpaceStr(usedStr);
            storeVolume.setPercentSpace(percent);

            long totalInode = storeVolume.getTotalInode();
            long availInode = storeVolume.getAvailInode();
            long usedInode = totalInode - availInode;
            double percentInode = Calculator.getPercentage(totalInode, availInode);
            storeVolume.setUsedInode(usedInode);
            storeVolume.setPercentInode(percentInode);
        });

        return storeVolumes;
    }
}
