package cd.casic.plugin;

import cd.casic.framework.commons.util.io.FileUtils;
import cd.casic.plugin.config.PluginProperties;
import cd.casic.plugin.dataobject.dao.PluginFacadeMemoryCache;
import cd.casic.plugin.dataobject.dto.PluginInformation;
import cd.casic.plugin.dataobject.pojo.PluginInfo;
import cd.casic.plugin.exception.PluginInstallationException;
import cd.casic.plugin.function.IPluginManager;
import cd.casic.plugin.function.PluginLifecycle;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.pf4j.DefaultPluginManager;
import org.pf4j.PluginState;
import org.pf4j.PluginWrapper;
import org.pf4j.RuntimeMode;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.stereotype.Component;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;


@Slf4j
@Component
public class PluginServiceImpl extends DefaultPluginManager implements
        IPluginManager,
        PluginLifecycle,
        InitializingBean,
        DisposableBean {

    public PluginServiceImpl(PluginProperties pluginProperties) {
        this.pluginsRoots.add(Paths.get(pluginProperties.getPluginRoot()));
        this.setRuntimeMode(pluginProperties.getRuntimeMode());
        super.initialize();
    }

    @Override
    protected void initialize() {
        // 这里不需要任何操作，只是覆盖父类的initialize方法
        // 如果不重写的话，在调用构造函数的时候会调用父类的无参构造函数，导致父类initialize方法被调用。
        // 父类initialize方法中会调用createPluginStatusProvider方法，该方法需要使用到PluginProperties，而此时还未被注入
    }

    @Override
    public PluginInfo install(Path path) throws Exception {
        /* 插件的目录
            工作目录
            │
            ├─plugins
            │  ├─example-mybatis-plugin@0.1 插件目录，id@version，存放jar、database、resource
            │  │  ├─ops-module-plugin-example-mybatis-plugin.jar 插件jar包，jar包命名不影响
            │  │  ├─database 存放sqlite数据库文件
            │  │  └─resource 存放静态资源
            │  └─example-redis-plugin@1.1.4
            │     ├─ops-module-plugin-example-redis-plugin.jar
            │     ├─database
            │     └─resource
         */
        String pluginId = StringUtils.EMPTY;
        try {
            beforeWork();
            pluginId = loadPlugin(path);
            PluginState pluginState = startPlugin(pluginId);
            log.info("install plugin [{}] success , plugin state {}", pluginId, pluginState.name());
            return PluginFacadeMemoryCache.getPluginInfo(pluginId);
        } catch (Exception e) {
            log.error("安装插件出现异常:{}", e.getMessage());
            Optional.of(pluginId).ifPresent(PluginFacadeMemoryCache::removePlugin);
            throw new PluginInstallationException(e.getMessage(), e.getLocalizedMessage(), e.getStackTrace());
        }
    }

    @Override
    public void installAfter(String pluginId) {
        try {
            super.stopPlugin(pluginId);
        } catch (Exception e) {
            log.error("install plugin after error:{}", e.getMessage());
        }
    }

    @Override
    public void unInstall(String pluginId, boolean isUpdate) throws Exception {
        // TODO unInstall的时候除掉插件资源
        log.info("unInstallPlugin:" + pluginId);
        PluginState pluginState = stopPlugin(pluginId);
        if (pluginState != PluginState.STOPPED) {
            throw new UnexpectedPluginStateException();
        }
        PluginInfo plugin = PluginFacadeMemoryCache.getPluginInfo(pluginId);
        plugin.clearApplicationContext();
        PluginFacadeMemoryCache.removePlugin(pluginId);
        //删除插件
        FileUtils.forceDelete(plugin.getPluginWrapper().getPluginPath().toFile());
    }

    @Override
    public void initPlugins(List<PluginInformation> plugins) throws Exception {

    }

    @Override
    public List<PluginWrapper> getInstallPlugins() {
        return null;
    }

    @Override
    public PluginInfo upgrade(String name, Path path) {
        return null;
    }

    @Override
    public PluginInfo reload(String name) {
        return null;
    }

    @Override
    public DataBuffer uglifyJsBundle() {
        return null;
    }

    @Override
    public DataBuffer uglifyCssBundle() {
        return null;
    }

    @Override
    public String generateBundleVersion() {
        return null;
    }

    @Override
    public Resource getJsBundle(String version) {
        return null;
    }

    @Override
    public Resource getCssBundle(String version) {
        return null;
    }

    @Override
    public PluginInfo changeState(String pluginName, boolean requestToEnable, boolean wait) {
        return null;
    }

    @Override
    public void destroy() throws Exception {

    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }

    @Override
    public void beforeWork() {

    }

    @Override
    public void AfterWork() {

    }


    private static class UnexpectedPluginStateException extends RuntimeException {
    }

    public void setRuntimeMode(RuntimeMode runtimeMode) {
        this.runtimeMode = runtimeMode;
    }
}