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.model.dto.ChannelProcessDto;
import cn.reghao.oss.console.app.rpc.RemoteService;
import cn.reghao.oss.console.util.AuthKeyContext;
import cn.reghao.oss.storeapi.dto.ObjectChannel;
import cn.reghao.oss.storeapi.iface.StoreService;
import cn.reghao.oss.console.account.service.UserContext;
import cn.reghao.oss.console.app.db.repository.UploadChannelRepository;
import cn.reghao.oss.console.app.model.dto.ChannelScopeDto;
import cn.reghao.oss.console.app.model.dto.UploadChannelDto;
import cn.reghao.oss.console.app.model.po.StoreNode;
import cn.reghao.oss.console.app.model.po.UploadChannel;
import cn.reghao.oss.console.app.model.vo.UploadChannelVo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
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 15:14:53
 */
@Service
public class UploadChannelService {
    private final UploadChannelRepository uploadChannelRepository;
    private final StoreNodeService storeNodeService;
    private final ByteConverter byteConverter = new ByteConverter();

    public UploadChannelService(UploadChannelRepository uploadChannelRepository, StoreNodeService storeNodeService) {
        this.uploadChannelRepository = uploadChannelRepository;
        this.storeNodeService = storeNodeService;
    }

    @Transactional(rollbackFor = Exception.class)
    public synchronized Result add(UploadChannelDto uploadChannelDto) {
        int nodeId = uploadChannelDto.getNodeId();
        StoreNode storeNode = storeNodeService.getById(nodeId);
        if (storeNode == null) {
            return Result.fail(String.format("store_node with id %s not exist", nodeId));
        }

        int createBy = UserContext.getUser().getId();
        //int createBy = 410;
        String channelPrefix = uploadChannelDto.getChannelPrefix();
        UploadChannel uploadChannel = uploadChannelRepository.findByPrefixAndCreateByAndNodeId(channelPrefix, createBy, nodeId);
        if (uploadChannel != null) {
            return Result.fail(String.format("channel_prefix %s exist", channelPrefix));
        }

        uploadChannel = uploadChannelRepository.findByPrefixAndNodeId(channelPrefix, nodeId);
        if (uploadChannel != null) {
            return Result.fail(String.format("someone has already created channel_prefix %s", channelPrefix));
        }

        int channelId = getNextChannelId(createBy);
        uploadChannel = new UploadChannel(channelId, uploadChannelDto, createBy);
        uploadChannelRepository.save(uploadChannel);

        String domain = storeNode.getDomain();
        String name = uploadChannel.getName();
        long maxSize = uploadChannel.getMaxSize();
        int fileType = uploadChannel.getFileType();
        boolean seturl = uploadChannel.getSeturl();
        int scope = uploadChannel.getScope();
        ObjectChannel channel = new ObjectChannel(channelId, name, channelPrefix, maxSize, fileType, seturl, scope, domain, createBy);

        String host = storeNode.getNodeAddr();
        int port = storeNode.getRpcPort();
        RemoteService<StoreService> remoteService = new RemoteService<>();
        StoreService storeService = remoteService.getService(host, port, StoreService.class);
        storeService.createChannel(createBy, channel);
        return Result.success();
    }

    private int getNextChannelId(int userId) {
        int channelId = 101;
        int total = uploadChannelRepository.countByCreateBy(userId);
        return channelId + total;
    }

    public void updateScope(ChannelScopeDto channelScopeDto) {
        int id = channelScopeDto.getId();
        int newScope = channelScopeDto.getNewScope();
        UploadChannel uploadChannel = uploadChannelRepository.findById(id).orElse(null);
        if (uploadChannel != null) {
            int currentScope = uploadChannel.getScope();
            if (currentScope != newScope) {
                uploadChannel.setScope(newScope);
                uploadChannelRepository.save(uploadChannel);
            }
        }
    }

    public void updateSeturl(ChannelProcessDto channelProcessDto) {
        int id = channelProcessDto.getId();
        UploadChannel uploadChannel = uploadChannelRepository.findById(id).orElse(null);
        if (uploadChannel != null) {
            boolean seturl = channelProcessDto.getSeturl();
            boolean current = uploadChannel.getSeturl();
            if (seturl != current) {
                uploadChannel.setSeturl(seturl);
                uploadChannelRepository.save(uploadChannel);

            }
        }
    }

    public Page<UploadChannelVo> getUploadChannels(PageRequest pageRequest) {
        int loginUser = UserContext.getUser().getId();
        Page<UploadChannel> page = uploadChannelRepository.findByCreateBy(loginUser, pageRequest);
        List<UploadChannelVo> list = page.stream().map(uploadChannel -> {
            long maxSize = uploadChannel.getMaxSize();
            String maxSizeStr = byteConverter.convert(ByteType.Bytes, maxSize);
            String bindDomain = storeNodeService.getById(uploadChannel.getNodeId()).getDomain();
            return new UploadChannelVo(uploadChannel, maxSizeStr, bindDomain);
        }).collect(Collectors.toList());

        return new PageImpl<>(list, pageRequest, page.getTotalElements());
    }

    public List<UploadChannel> getByDomain(String domain) {
        StoreNode storeNode = storeNodeService.getByDomain(domain);
        int nodeId = storeNode.getId();
        return uploadChannelRepository.findByNodeId(nodeId);
    }

    public UploadChannel getUploadChannel(int id) {
        return uploadChannelRepository.findById(id).orElse(null);
    }

    public UploadChannel getByChannelIdAndCreateBy(int channelId, int createBy) {
        return uploadChannelRepository.findByChannelIdAndCreateBy(channelId, createBy);
    }

    public ObjectChannel getObjectChannelByChannelId(int channelId, int createBy) {
        UploadChannel uploadChannel = uploadChannelRepository.findByChannelIdAndCreateBy(channelId, createBy);
        return getObjectChannel(uploadChannel);
    }

    public ObjectChannel getObjectChannel(UploadChannel uploadChannel) {
        int channelId = uploadChannel.getChannelId();
        String name = uploadChannel.getName();
        String channelPrefix = uploadChannel.getPrefix();
        long maxSize = uploadChannel.getMaxSize();
        int fileType = uploadChannel.getFileType();
        boolean seturl = uploadChannel.getSeturl();
        int scope = uploadChannel.getScope();
        String domain = storeNodeService.getById(uploadChannel.getNodeId()).getDomain();
        int createBy = uploadChannel.getCreateBy();
        return new ObjectChannel(channelId, name, channelPrefix, maxSize, fileType, seturl, scope, domain, createBy);
    }

    public StoreNode getStoreNodeByChannelId(int channelId) throws Exception {
        int userId = AuthKeyContext.getUser();
        UploadChannel uploadChannel = uploadChannelRepository.findByChannelIdAndCreateBy(channelId, userId);
        if (uploadChannel == null) {
            String errMsg = String.format("channel_id %s not exist", channelId);
            throw new Exception(errMsg);
        }

        String domain = storeNodeService.getById(uploadChannel.getNodeId()).getDomain();
        String ossUrl = String.format("https://%s", domain);
        long maxSize = uploadChannel.getMaxSize();

        StoreNode storeNode = storeNodeService.getByDomain(domain);
        if (storeNode == null) {
            String errMsg = String.format("store_node %s not exist", domain);
            throw new Exception(errMsg);
        }

        return storeNode;
    }

    public List<UploadChannel> getByNodeIdAndUserId(int nodeId, int userId) {
        return uploadChannelRepository.findByNodeIdAndCreateBy(nodeId, userId);
    }

    public List<UploadChannel> getByDomainAndCreateBy(String domain, int createBy) {
        StoreNode storeNode = storeNodeService.getByDomain(domain);
        int nodeId = storeNode.getId();
        return getByNodeIdAndUserId(nodeId, createBy);
    }
}
