package com.friday.executor.hotdeploy;

import com.google.common.collect.Lists;
import com.friday.base.common.response.DubboResult;
import com.friday.dynamic.core.ModuleRuntimeException;
import com.friday.dynamic.core.ModuleService;
import com.friday.executor.util.UnzipUtils;
import com.friday.operation.api.executor.dto.module.InvokeDto;
import com.friday.operation.api.executor.dto.module.ModuleDto;
import com.friday.operation.api.executor.dto.module.ModuleFilter;
import com.friday.operation.api.executor.dto.module.ModuleRegisterDto;
import com.friday.operation.api.executor.service.IDynamicModuleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.friday.executor.constant.ModuleErrorCode.*;

/**
 * @Author YaoYao
 * @Date Created In 18:57 2022/11/23
 * @Description
 * @Modified By
 */
@Service
@Slf4j
public class DynamicModuleService implements IDynamicModuleService {

    @Autowired
    private ModuleService moduleService;
    @Value("${dynamic.jar}")
    private String rootJarPath;
    @Value("${system.file.path.separator}")
    private String filepathSeparator;

    @Override
    public DubboResult<ModuleDto> registerModule(ModuleRegisterDto register) {
        try {
            String localJarFilePath = this.getLocalJarFilePath(register);
            DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(localJarFilePath), "*.jar");
            stream.forEach(s -> moduleService.loadAndRegister(s));
            return DubboResult.newSuccess(new ModuleDto());
        } catch (Exception e){
            log.error(REGISTER_MODULE_ERROR.getCode(), e);
            return DubboResult.newFailure(REGISTER_MODULE_ERROR.getCode(), REGISTER_MODULE_ERROR.getMessage());
        }
    }

    private String getLocalJarFilePath(ModuleRegisterDto register) throws IOException {
        String destFilePath = this.rootJarPath + register.getModule() + filepathSeparator + register.getVersion() + filepathSeparator;
        File destFile = new File(destFilePath);
        if (!destFile.exists()) {
            destFile.mkdirs();
        }
        File destZipFile =  new File(destFilePath + filepathSeparator + "test.zip");
        this.copyFile(new File(register.getFileKey()), destZipFile);
        UnzipUtils.unZip(destZipFile, destFilePath);
        Files.delete(Paths.get(destFilePath + filepathSeparator + "test.zip"));
        return destFilePath;
    }

    private void copyFile(File source, File dest) throws IOException {
        Files.copy(source.toPath(), dest.toPath());
    }

    @Override
    public DubboResult invoke(InvokeDto invoke) {
        try {
            return moduleService.handle(invoke.getModuleName(), invoke.getModuleVersion(), invoke.getHandlerName(), invoke.getMethodName(), invoke.getInputArg());
        } catch (Exception e){
            log.error(INVOKE_MODULE_ERROR.getCode(), e);
            return DubboResult.newFailure(INVOKE_MODULE_ERROR.getCode(), INVOKE_MODULE_ERROR.getMessage());
        }
    }

    @Override
    public DubboResult removeModule(String moduleName, String moduleVersion) {
        try {
            moduleService.removeAndDestroy(moduleName, moduleVersion);
            return DubboResult.newSuccess();
        } catch (Exception e){
            log.error(REMOVE_MODULE_ERROR.getCode(), e);
            return DubboResult.newFailure(REMOVE_MODULE_ERROR.getCode(), REMOVE_MODULE_ERROR.getMessage());
        }
    }

    @Override
    public DubboResult<ArrayList<ModuleDto>> queryModules(ModuleFilter filter) {
        List<ModuleDto> result = moduleService.queryAllModule();
        if(Objects.nonNull(filter)){
            if(StringUtils.isNotEmpty(filter.getName())){
                result = result.stream().filter(m -> m.getName().contains(filter.getName())).collect(Collectors.toList());
            }
            if(StringUtils.isNotEmpty(filter.getVersion())){
                result = result.stream().filter(m -> m.getVersion().contains(filter.getVersion())).collect(Collectors.toList());
            }
        }
        return DubboResult.newSuccess(Lists.newArrayList(result));
    }
}
