/*
 * Copyright (C) 2016-2022 ActionTech.
 * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
 */

package com.actiontech.dble.config.util;

import com.actiontech.dble.DbleServer;
import com.actiontech.dble.backend.datasource.PhysicalDbGroup;
import com.actiontech.dble.backend.datasource.PhysicalDbInstance;
import com.actiontech.dble.cluster.logic.ClusterLogic;
import com.actiontech.dble.cluster.zkprocess.entity.DbGroups;
import com.actiontech.dble.cluster.zkprocess.entity.dbGroups.DBGroup;
import com.actiontech.dble.cluster.zkprocess.entity.dbGroups.DBInstance;
import com.actiontech.dble.config.converter.DBConverter;
import com.actiontech.dble.singleton.HaConfigManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by szf on 2019/10/23.
 */
public class DbXmlWriteJob implements Runnable {
    private static final Logger LOGGER = LoggerFactory.getLogger(PhysicalDbGroup.class);
    private final Set<PhysicalDbGroup> changeSet;
    private final DbGroups dbGroups;
    private volatile boolean finish = false;
    private volatile String errorMessage = null;
    private final ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private final int reloadIndex;

    public DbXmlWriteJob(Set<PhysicalDbGroup> changeSet, DbGroups dbGroups, int reloadIndex) {
        this.changeSet = changeSet;
        this.dbGroups = dbGroups;
        this.reloadIndex = reloadIndex;
    }

    @Override
    public void run() {
        try {
            List<DBGroup> dbGroupList = dbGroups.getDbGroup();
            Map<String, DBGroup> dbGroupBeanMap = ClusterLogic.forHA().changeFromListToMap(dbGroupList);
            for (PhysicalDbGroup dbGroup : changeSet) {
                DBGroup dbGroupBean = dbGroupBeanMap.get(dbGroup.getGroupName());
                if (dbGroupBean != null) {
                    changeHostInfo(dbGroupBean, dbGroup);
                } else {
                    LOGGER.warn("dbGroup " + dbGroup.getGroupName() + " is not found");
                }
            }
            HaConfigManager.getInstance().write(dbGroups, reloadIndex);
            DbleServer.getInstance().getConfig().setDbConfig(DBConverter.dbBeanToJson(dbGroups));
        } catch (Exception e) {
            errorMessage = e.getMessage();
            HaConfigManager.getInstance().log("get error from SchemaWriteJob", e);
        } finally {
            HaConfigManager.getInstance().finishAndNext();
            this.signalAll();
        }
    }


    private void changeHostInfo(DBGroup dbGroupBean, PhysicalDbGroup dbGroup) {
        Map<String, PhysicalDbInstance> physicalDbInstances = dbGroup.getAllDbInstanceMap();
        for (DBInstance dbInstanceBean : dbGroupBean.getDbInstance()) {
            PhysicalDbInstance dbInstance = physicalDbInstances.get(dbInstanceBean.getName());
            if (dbInstance != null) {
                dbInstanceBean.setPrimary(!dbInstance.isReadInstance());
                dbInstanceBean.setDisabled(dbInstance.isDisabled() ? "true" : null);
            } else {
                LOGGER.warn("dbInstance " + dbInstanceBean.getName() + " is not found");
            }
        }
    }


    public void waitForWritingDone() {
        lock.lock();
        try {
            while (!finish) {
                condition.await();
            }
        } catch (InterruptedException e) {
            LOGGER.info("unexpected error:", e);
        } finally {
            lock.unlock();
        }

        if (errorMessage != null) {
            LOGGER.info("get result errorMessage = " + errorMessage);
            throw new RuntimeException(errorMessage);
        }
    }

    public void signalAll() {
        lock.lock();
        try {
            finish = true;
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}
