package cn.reghao.tnb.oss.store.rpc;

import cn.reghao.jutil.jdk.web.db.PageList;
import cn.reghao.tnb.auth.model.OssPayload;
import cn.reghao.jutil.jdk.security.RandomString;
import cn.reghao.tnb.oss.api.constant.ChannelAction;
import cn.reghao.tnb.oss.api.constant.ObjectScope;
import cn.reghao.tnb.oss.api.dto.disk.DiskVolume;
import cn.reghao.tnb.oss.api.dto.ObjectInfo;
import cn.reghao.tnb.oss.api.dto.media.AudioInfo;
import cn.reghao.tnb.oss.api.dto.media.ConvertedImageInfo;
import cn.reghao.tnb.oss.api.dto.media.ImageInfo;
import cn.reghao.tnb.oss.api.dto.media.VideoInfo;
import cn.reghao.tnb.auth.JwtUtil;
import cn.reghao.tnb.oss.store.db.repository.ObjectRepository;
import cn.reghao.tnb.oss.store.model.po.FileMeta;
import cn.reghao.tnb.oss.api.dto.ObjectMeta;
import cn.reghao.tnb.oss.store.service.DiskService;
import cn.reghao.tnb.oss.store.service.FileStoreService;
import cn.reghao.tnb.oss.store.service.ObjectNameService;
import cn.reghao.tnb.oss.api.iface.StoreService;
import cn.reghao.tnb.oss.store.service.SignService;
import cn.reghao.tnb.oss.store.task.MediaFileProcessor;
import com.github.benmanes.caffeine.cache.Cache;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Service;

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

/**
 * 获取存储节点信息
 * oss-console 调用
 *
 * @author reghao
 * @date 2023-08-01 14:54:23
 */
@DubboService
@Service
public class StoreServiceImpl implements StoreService {
    private final ObjectNameService objectNameService;
    private final FileStoreService fileStoreService;
    private final ObjectRepository objectRepository;
    private final SignService signService;
    private final MediaFileProcessor mediaFileProcessor;
    private final Cache<String, String> cache;
    private final DiskService diskService;

    public StoreServiceImpl(ObjectNameService objectNameService, FileStoreService fileStoreService,
                            ObjectRepository objectRepository, SignService signService,
                            MediaFileProcessor mediaFileProcessor, Cache<String, String> cache,
                            DiskService diskService) {
        this.objectNameService = objectNameService;
        this.fileStoreService = fileStoreService;
        this.objectRepository = objectRepository;
        this.signService = signService;
        this.mediaFileProcessor = mediaFileProcessor;
        this.cache = cache;
        this.diskService = diskService;
    }

    @Override
    public List<DiskVolume> getDiskVolumes() {
        Map<String, DiskVolume> map = diskService.getFileStore().stream()
                .collect(Collectors.toMap(DiskVolume::getVolume, k -> k));
        List<DiskVolume> list = fileStoreService.getStoreDisks().stream()
                .map(localStore -> {
                    String volume = localStore.getDiskVolume();
                    String storeDir = localStore.getStoreDir();
                    DiskVolume diskVolume = map.get(volume);
                    diskVolume.setStoreDir(storeDir);
                    return diskVolume;
                })
                .collect(Collectors.toList());
        return list;
    }

    @Override
    public String getUploadToken(int channelCode, int owner, int expire) {
        String secretKey = RandomString.getString(128);
        String action = ChannelAction.upload.getName();
        long expireAt = System.currentTimeMillis() + expire*1000L;
        OssPayload ossPayload = new OssPayload(channelCode, action, owner);
        String uploadToken = JwtUtil.createOssToken(ossPayload, expireAt, secretKey);
        cache.put(uploadToken, secretKey);
        return uploadToken;
    }

    @Override
    public void createChannel(int owner, String channelPrefix, int scope) {
        ObjectScope objectScope = ObjectScope.getByCode(scope);
        if (objectScope == null) {
        }
        objectNameService.createParentDirs(owner, channelPrefix, scope);
    }

    @Override
    public int countChannelObjects(String channelPrefix, int owner) {
        FileMeta fileMeta = objectRepository.getByObjectName(channelPrefix, owner);
        int total = 0;
        if (fileMeta != null) {
            total = objectRepository.countByPid(fileMeta.getObjectId());
        }

        return total;
    }

    @Override
    public void setObjectUpload(String objectId) {
        FileMeta fileMeta = objectRepository.getFileMeta(objectId);
        if (fileMeta != null) {
            fileMeta.setDeleted(false);
            objectRepository.updateFileMetaDeleted(fileMeta);
        }
    }

    @Override
    public void setObjectScope(String objectId, int scope) {
        FileMeta fileMeta = objectRepository.getByObjectId(objectId);
        objectRepository.updateObjectScope(scope, fileMeta.getObjectName());
    }

    @Override
    public void deleteByObjectId(String objectId) {
        FileMeta fileMeta = objectRepository.getByObjectId(objectId);
        if (fileMeta != null) {
            fileMeta.setDeleted(true);
            objectRepository.updateFileMetaDeleted(fileMeta);
        }
    }

    @Override
    public void deleteByObjectName(String objectName, int owner) {
        FileMeta fileMeta = objectRepository.getByObjectName(objectName, owner);
        if (fileMeta != null) {
            fileMeta.setDeleted(true);
            objectRepository.updateFileMetaDeleted(fileMeta);
        }
    }

    @Override
    public ObjectInfo getObjectInfo(String objectId) throws Exception {
        FileMeta fileMeta = objectRepository.getByObjectId(objectId);
        if (fileMeta == null) {
            String errMsg = String.format("%s not exist in oss-store", objectId);
            throw new Exception(errMsg);
        }

        return getObjectInfo(fileMeta);
    }

    private ObjectInfo getObjectInfo(FileMeta fileMeta) {
        String objectId = fileMeta.getObjectId();
        String objectName = fileMeta.getObjectName();
        int fileType = fileMeta.getFileType();
        String filename = fileMeta.getFilename();
        long size = fileMeta.getSize();
        int scope = fileMeta.getScope();
        String sha256sum = fileMeta.getSha256sum();
        return new ObjectInfo(objectId, objectName, fileType, filename, size, scope, sha256sum);
    }

    @Override
    public String getSignedUrl(String domain, int owner, String objectId, int expire) throws Exception {
        ObjectMeta objectMeta = objectRepository.getObjectMetaById(objectId);
        if (objectMeta == null) {
            String errMsg = String.format("objectId %s not exist in oss-store", objectId);
            throw new Exception(errMsg);
        }

        String url = String.format("//%s/%s", domain, objectMeta.getObjectName());
        return signService.getSignedUrl(owner, url, expire);
    }

    @Override
    public String getSignedUrl(String objectUrl, int owner, int expire) throws Exception {
        // //oss.reghao.cn/file/abc/def
        String objectUrl1 = objectUrl.replace("//", "");
        int idx = objectUrl1.indexOf("/");
        String domain = objectUrl1.substring(0, idx);
        String objectName = objectUrl1.substring(idx+1);
        ObjectMeta objectMeta = objectRepository.getObjectMetaByName(objectName, owner);
        if (objectMeta == null) {
            String errMsg = String.format("objectName %s not exist in oss-store", objectName);
            throw new Exception(errMsg);
        }
        return signService.getSignedUrl(owner, objectUrl, expire);
    }

    @Override
    public VideoInfo getVideoInfo(String objectId) throws Exception {
        VideoInfo videoInfo = mediaFileProcessor.getVideoInfo(objectId);
        return videoInfo;
    }

    @Override
    public ImageInfo getImageInfo(String objectId) throws Exception {
        ImageInfo imageInfo = mediaFileProcessor.getImageInfo(objectId);
        return imageInfo;
    }

    @Override
    public ConvertedImageInfo getWebpInfo(String objectId) throws Exception {
        return mediaFileProcessor.getWebpInfo(objectId);
    }

    @Override
    public AudioInfo getAudioInfo(String objectId) throws Exception {
        AudioInfo audioInfo = mediaFileProcessor.getAudioInfo(objectId);
        return audioInfo;
    }

    @Override
    public PageList<ObjectInfo> getByPrefix(int owner, String objectName, int pn, int ps) {
        String pid = "0";
        if (!objectName.isBlank()) {
            FileMeta fileMeta = objectRepository.getByObjectName(objectName, owner);
            if (fileMeta != null) {
                pid = fileMeta.getObjectId();
            }
        }

        int total = objectRepository.countByPid(pid);
        List<ObjectInfo> list = objectRepository.getObjectsByPage(pid, pn, ps);
        return PageList.pageList(pn, ps, total, list);
    }
}
