package cn.reghao.oss.console.app.service;

import cn.reghao.jutil.jdk.converter.ByteConverter;
import cn.reghao.jutil.jdk.converter.ByteType;
import cn.reghao.jutil.jdk.result.Result;
import cn.reghao.oss.console.app.db.repository.StoreNodeRepository;
import cn.reghao.oss.console.app.db.repository.UserNodeRepository;
import cn.reghao.oss.console.app.model.dto.NodeUpdateDto;
import cn.reghao.oss.console.app.model.po.StoreDisk;
import cn.reghao.oss.console.app.model.po.StoreNode;
import cn.reghao.oss.console.app.model.vo.KeyValue;
import cn.reghao.oss.console.app.model.vo.StoreDiskVO;
import cn.reghao.oss.console.app.model.vo.StoreNodeVO;
import cn.reghao.oss.console.app.rpc.RpcService;
import cn.reghao.oss.storeapi.dto.StoreDiskDto;
import cn.reghao.oss.storeapi.dto.StoreInfo;
import cn.reghao.oss.storeapi.dto.StoreNodeDto;
import cn.reghao.oss.storeapi.dto.StoreProperties;
import cn.reghao.oss.storeapi.iface.StoreService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author reghao
 * @date 2024-02-23 13:01:38
 */
@Service
public class StoreNodeService {
    private final StoreNodeRepository storeNodeRepository;
    private final RpcService rpcService;
    private final UserNodeRepository userNodeRepository;
    private ByteConverter byteConverter = new ByteConverter();

    public StoreNodeService(StoreNodeRepository storeNodeRepository, RpcService rpcService,
                            UserNodeRepository userNodeRepository) {
        this.storeNodeRepository = storeNodeRepository;
        this.rpcService = rpcService;
        this.userNodeRepository = userNodeRepository;
    }

    public void add(StoreNodeDto storeNodeDto) {
        String nodeAddr = storeNodeDto.getNodeAddr();
        StoreNode storeNode = storeNodeRepository.findByNodeAddr(nodeAddr);
        if (storeNode == null) {
            storeNode = new StoreNode(storeNodeDto);
            storeNodeRepository.save(storeNode);
        } else {
            List<StoreDisk> storeDisks = storeNodeDto.getDiskDtoList().stream()
                    .map(StoreDisk::new)
                    .collect(Collectors.toList());
            storeNode.setStoreDisks(storeDisks);
            storeNodeRepository.save(storeNode);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public Result updateDomain(NodeUpdateDto nodeUpdateDto) {
        int id = nodeUpdateDto.getId();
        StoreNode storeNode = storeNodeRepository.findById(id).orElse(null);
        if (storeNode == null) {
            return Result.fail(String.format("id %s not exist", id));
        }

        String domain = nodeUpdateDto.getDomain();
        StoreNode entity = storeNodeRepository.findByDomain(domain);
        if (entity != null) {
            return Result.fail(String.format("domain %s exist", domain));
        }

        if (domain.equals(storeNode.getDomain())) {
            return Result.success();
        }

        String secretKey = nodeUpdateDto.getSecretKey();
        String referer = nodeUpdateDto.getReferer();
        storeNode.setDomain(domain);
        storeNode.setSecretKey(secretKey);
        storeNode.setReferer(referer);
        storeNode.setEnabled(true);
        storeNodeRepository.save(storeNode);

        StoreProperties storeProperties = new StoreProperties(domain, secretKey, referer);
        StoreService storeService = rpcService.getStoreService(storeNode);
        storeService.updateStoreProperties(storeProperties);
        return Result.success();
    }

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

        int total = userNodeRepository.countByNodeId(nodeId);
        if (total > 0) {
            return Result.fail("someone user the node");
        }

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

    public Page<StoreNode> getByPage(PageRequest pageRequest) {
        return storeNodeRepository.findAll(pageRequest);
    }

    public List<StoreNode> getByIds(List<Integer> list) {
        return storeNodeRepository.findAllById(list);
    }

    public StoreNode getByDomain(String domain) {
        return storeNodeRepository.findByDomain(domain);
    }

    public StoreNode getByNodeAddr(String nodeAddr) {
        return storeNodeRepository.findByNodeAddr(nodeAddr);
    }

    public StoreNode getById(int id) {
        return storeNodeRepository.findById(id).orElse(null);
    }

    public List<KeyValue> getNodeKeyValues() {
        return storeNodeRepository.findAll().stream()
                .map(storeNode -> {
                    int nodeId = storeNode.getId();
                    String domain = storeNode.getDomain();
                    return new KeyValue(nodeId+"", domain);
                }).collect(Collectors.toList());
    }

    public StoreNodeVO getDetail(int nodeId) {
        StoreNode storeNode = storeNodeRepository.findById(nodeId).orElse(null);
        if (storeNode == null) {
            return null;
        }

        StoreService storeService = rpcService.getStoreService(storeNode);
        StoreInfo storeInfo = storeService.getStoreInfo();
        List<StoreDiskDto> storeDiskDtos = storeInfo.getStoreDisks();
        /*List<StoreDisk> list = storeDiskDtos.stream()
                .map(StoreDisk::new)
                .collect(Collectors.toList());
        storeNode.setStoreDisks(list);
        storeNodeRepository.save(storeNode);*/

        List<StoreDiskVO> storeDisks = storeDiskDtos.stream()
                .map(storeDisk -> {
                    String diskDir = storeDisk.getDiskDir();
                    long total = storeDisk.getTotal();
                    String totalStr = byteConverter.convert(ByteType.Bytes, total);
                    long avail = storeDisk.getAvail();
                    String availStr = byteConverter.convert(ByteType.Bytes, avail);
                    return new StoreDiskVO(diskDir, totalStr, availStr);
                }).collect(Collectors.toList());

        return new StoreNodeVO(storeNode, storeDisks, storeInfo);
    }
}
