package com.bocloud.cmp.provider;

import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.model.Butler;
import com.bocloud.cmp.provider.listener.ButlerEventListener;
import com.bocloud.common.encrypt.DESEncrptor;
import com.bocloud.common.encrypt.Encryptor;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.utils.JSONTools;
import io.jsonwebtoken.lang.Assert;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 云平台配置信息缓存类
 *
 * @author dmw
 */
public class ButlerRepository {
    private static Logger logger = LoggerFactory.getLogger(ButlerRepository.class);
    static final String DEFAULT_KEY = "BocloudCMP";
    static final String CODE_PREFIX = "bocloud.cmp.";
    /**
     * 云平台配置信息缓存
     */
    private Map<String, Butler> butlers;
    static Encryptor encryptor = new DESEncrptor();
    static ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    static Lock readLock = rwLock.readLock();
    static Lock writeLock = rwLock.writeLock();
    static Set<ButlerEventListener> listeners = new HashSet<>();

    /**
     *
     */
    private ButlerRepository() {
        super();
        if (null != RepositoryContainer.instance) {
            // 防止利用反射机制实现多个对象的漏洞
            throw new RuntimeException("ButlerRepository遭受反射攻击！");
        }
        butlers = new ConcurrentHashMap<>();
    }

    /**
     * 获取配置实例方法，即将被废弃
     *
     * @return
     */
    public static ButlerRepository instance() {
        return RepositoryContainer.instance;
    }

    /**
     * 获取配置实例标准方法
     *
     * @return
     */
    public static ButlerRepository getInstance() {
        return RepositoryContainer.instance;
    }

    /**
     * 更新云平台配置
     *
     * @param data
     */
    public void update(String data) {
        Assert.notNull(data, "Cloud vendor data is null");
        BocloudResult<JSONObject> result = JSONTools.isJSON(data);
        if (result.isFailed()) {
            logger.error("Update config error: 传入参数格式化错误！");
            return;
        }
        JSONObject jsonObject = result.getData();
        Set<String> butlerKeys = jsonObject.keySet();
        BocloudResult<Butler> butlerResult;
        writeLock.lock();
        try {
            Butler butler;
            Set<String> oldKeySet = butlers.keySet();
            for (String key : oldKeySet) {
                if (butlerKeys.contains(key)) {
                    continue;
                }
                Butler old = butlers.get(key);
                notifyListener(buildCode(old), Action.REMOVE, old);
                butlers.remove(key);
            }
            for (String key : butlerKeys) {
                butlerResult = Butler.convert(jsonObject.getString(key));
                if (butlerResult.isFailed()) {
                    continue;
                }
                butler = butlerResult.getData();
                butlers.put(key, butler);
                notifyListener(buildCode(butler), Action.UPDATE, this.get(butler.getUuid()));
            }
        } catch (Exception e) {
            logger.error("update config error:", e);
        } finally {
            writeLock.unlock();
        }
    }

    /**
     * 获取云平台配置
     *
     * @param uuid 云平台UUID
     * @return
     */
    public Butler get(String uuid) {
        readLock.lock();
        try {
            Butler butler = butlers.get(uuid);
            if (null == butler) {
                return null;
            }
            Butler newButler = new Butler();
            BeanUtils.copyProperties(newButler, butler);
            if (Objects.isNull(butler.getCategory())) {
            	newButler.setCategory("UNKNOWN");
            }
            if (!StringUtils.isEmpty(butler.getPassword())) {
            	newButler.setPassword(encryptor.decrypt(butler.getPassword(), DEFAULT_KEY));
            }
            if (!StringUtils.isEmpty(butler.getAccessKey())) {
            	newButler.setAccessKey(encryptor.decrypt(butler.getAccessKey(), DEFAULT_KEY));
            }
            if (!StringUtils.isEmpty(butler.getSecretKey())) {
            	newButler.setSecretKey(encryptor.decrypt(butler.getSecretKey(), DEFAULT_KEY));
            }
            return newButler;
        } catch (Exception e) {
            logger.error("get config error:", e);
            return null;
        } finally {
            readLock.unlock();
        }
    }

    /**
     * 更新指定key的云平台
     *
     * @param key
     * @param butler
     */
    public void update(String key, Butler butler) {
        if (StringUtils.isEmpty(key) || null == butler) {
            return;
        }
        writeLock.lock();
        try {
            butlers.put(key, butler);
            notifyListener(buildCode(butler), Action.REMOVE, butler);
        } catch (Exception e) {
            logger.error("update key【{}】 butler exception ", key, e);
        } finally {
            writeLock.unlock();
        }
    }

    public void remove(String key) {
        if (StringUtils.isEmpty(key)) {
            return;
        }
        writeLock.lock();
        try {
            if (butlers.containsKey(key) && null != butlers.get(key)) {
                Butler butler = butlers.get(key);
                notifyListener(buildCode(butler), Action.REMOVE, butler);
                butlers.remove(key);
            }
        } catch (Exception e) {
            logger.error("remove key【{}】 butler exception ", key, e);
        } finally {
            writeLock.unlock();
        }
    }

    /**
     * 添加云平台监听器
     *
     * @param listener
     */
    public void addListener(ButlerEventListener listener) {
        if (null != listener) {
            listeners.add(listener);
        }
    }

    /**
     * 查看指定code的监听器是否存在 code:bocloud.cmp.openstack
     *
     * @param code
     * @return
     */
    public boolean contains(String code) {
        for (ButlerEventListener listener : listeners) {
            if (listener.getCode().equalsIgnoreCase(code)) {
                return true;
            }
        }
        return false;
    }

    private static void notifyListener(String listenerCode, Action action, Butler butler) {
        for (ButlerEventListener listener : listeners) {
            if (!listenerCode.equalsIgnoreCase(listener.getCode())) {
                continue;
            }
            notify(listener, action, butler);
        }
    }

    public static void notifyListeners(Action action, Butler butler) {
        for (ButlerEventListener listener : listeners) {
            notify(listener, action, butler);
        }
    }

    private static void notify(ButlerEventListener listener, Action action, Butler butler) {
        switch (action) {
            case REMOVE:
                listener.onRemoved(butler);
                break;
            case UPDATE:
                listener.onChanged(butler);
                break;
            default:
                listener.onChanged(butler);
                break;
        }
    }

    enum Action {
        UPDATE, REMOVE;
    }

    private String buildCode(Butler butler) {
        if (null == butler || StringUtils.isEmpty(butler.getCategory())) {
            return null;
        } else {
            return (CODE_PREFIX + butler.getCategory()).toLowerCase();
        }
    }

    private static class RepositoryContainer {
        private static ButlerRepository instance = new ButlerRepository();
    }
}
