/*
 * Copyright © 2025 by Data Virtuality GmbH
 */
package org.teiid.events;

import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.metadata.*;
import org.teiid.query.tempdata.TempTableDataManager;

import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

/**
 * @author Mate Simovic
 * @date 2025.05.13
 */
public class DVDistributor {
    
    @FunctionalInterface
    public interface Action {
        void execute();
    }

    public static class SingletonHolder {
        public static final DVDistributor HOLDER_INSTANCE = new DVDistributor();
    }

    public static DVDistributor getInstance() {
        return DVDistributor.SingletonHolder.HOLDER_INSTANCE;
    }

    private EventDistributor eventDistributor;
    private TempTableDataManager dtm;

    public void init(TempTableDataManager dtm) {
        this.eventDistributor = dtm != null && dtm.getEventDistributor() != null 
                ? dtm.getEventDistributor() 
                : null;
        this.dtm = dtm;
    }

    public void updateMatViewRow(String vdbName, int vdbVersion, String schema,
                                 String viewName, List<?> tuple, boolean delete, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.updateMatViewRow(vdbName, vdbVersion, schema, viewName, tuple, delete);
    }

    public void dataModification(boolean distribute, String vdbName, int vdbVersion, String schema,	String... tableNames) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.dataModification(vdbName, vdbVersion, schema, tableNames);
    }
    
    public void setColumnStats(String vdbName, int vdbVersion,
                               String schemaName, String tableName, String columnName,
                               ColumnStats stats, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.setColumnStats(vdbName, vdbVersion, schemaName, tableName, columnName, stats);
    }

    public void setTableStats(String vdbName, int vdbVersion,
                              String schemaName, String tableName, TableStats stats, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.setTableStats(vdbName, vdbVersion, schemaName, tableName, stats);
    }

    public void setInsteadOfTriggerDefinition(String vdbName, int vdbVersion,
                                              String schema, String viewName, Table.TriggerEvent triggerEvent,
                                              String triggerDefinition, Boolean enabled, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.setInsteadOfTriggerDefinition(vdbName, vdbVersion, schema, viewName, triggerEvent, triggerDefinition, enabled);
    }

    public void setProcedureDefinition(String vdbName, int vdbVersion,String schema, String procName, String definition, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.setProcedureDefinition(vdbName, vdbVersion, schema, procName, definition);
    }

    public void setViewDefinition(String vdbName, int vdbVersion, String schema, String viewName, String definition, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.setViewDefinition(vdbName, vdbVersion, schema, viewName, definition);
    }

    public void setProperty(String vdbName, int vdbVersion, String uuid,
                            String name, String value, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.setProperty(vdbName, vdbVersion, uuid, name, value);
    }

    public void createViewProc(String vdbName, int vdbVersion, String command, boolean view, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.createViewProc(vdbName, vdbVersion, command, view);
    }

    public void alterViewProc(String vdbName, int vdbVersion, String command, boolean view, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.alterViewProc(vdbName, vdbVersion, command, view);
    }

    public void dropViewProc(String vdbName, int vdbVersion, String command, Object viewProc, Supplier<Integer> id, Supplier<String> schema, Supplier<String> resourceName, boolean view, boolean distribute) {
        if (distribute && eventDistributor != null) {
            if (viewProc != null)
                this.eventDistributor.dropViewProc(vdbName, vdbVersion, command, id.get(), schema.get(), resourceName.get(), view);
            else
                this.eventDistributor.dropViewProc(vdbName, vdbVersion, command, view);
        }
    }

    public void importViewProc(String vdbName, int vdbVersion, String sql, boolean view, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.importViewProc(vdbName, vdbVersion, sql, view);
    }

    public void setFunctionDefinition(String vdbName, int vdbVersion,String schema, String funcName, String definition, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.setFunctionDefinition(vdbName, vdbVersion, schema, funcName, definition);
    }

    public void createFunction(String vdbName, int vdbVersion, String command, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.createFunction(vdbName, vdbVersion, command);
    }

    public void alterFunction(String vdbName, int vdbVersion, String command, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.alterFunction(vdbName, vdbVersion, command);
    }

    public void dropFunction(String vdbName, int vdbVersion, String command, Object function, Supplier<Integer> id, Supplier<String> schema, Supplier<String> resourceName, boolean distribute) {
        if (distribute && eventDistributor != null) {
            if (function != null)
                this.eventDistributor.dropFunction(vdbName, vdbVersion, command, id.get(), schema.get(), resourceName.get());
            else
                this.eventDistributor.dropFunction(vdbName, vdbVersion, command);
        }
    }

    public void importFunction(String vdbName, int vdbVersion, String sql, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.importFunction(vdbName, vdbVersion, sql);
    }

    public void createConnection(String vdbName, int vdbVersion, String name, String template, String properties, String encryptedProperties, boolean distribute) {
        if (distribute && eventDistributor != null) {
            LogManager.logInfo(LogConstants.CTX_CONNECTOR, "DVController - Connection creation: " + name + " distributing...");
            this.eventDistributor.createConnection(vdbName, vdbVersion, name, template, properties, encryptedProperties);
            LogManager.logInfo(LogConstants.CTX_CONNECTOR, "DVController - Connection creation: " + name + " is distributed");
        }
    }

    public void createDatasource(String vdbName, int vdbVersion, String name, String connectionName, String translator, String modelPropsString, String translatorPropsString, String encryptedModelPropsString, String encryptedTranslatorPropsString, boolean distribute, Action doOnInitiatorNode) {
        if (distribute) {
            doOnInitiatorNode.execute();
            if (eventDistributor != null) {
                LogManager.logInfo(LogConstants.CTX_CONNECTOR, "DVController - Datasource creation: " + name + " distributing...");
                this.eventDistributor.createDatasource(vdbName, vdbVersion, name, connectionName, translator, modelPropsString, translatorPropsString, encryptedModelPropsString, encryptedTranslatorPropsString);
                LogManager.logInfo(LogConstants.CTX_CONNECTOR, "DVController - Datasource creation: " + name + " is distributed");
            }
        }
    }

    public void removeConnection(String vdbName, int vdbVersion, String name, String jndiName, String dropScript, String template, boolean distribute) {
        if (distribute && eventDistributor != null) {
            LogManager.logInfo(LogConstants.CTX_CONNECTOR, "DVController - Dropping connection: " + name + " distributing...");
            this.eventDistributor.removeConnection(vdbName, vdbVersion, name, jndiName, dropScript, template);
            LogManager.logInfo(LogConstants.CTX_CONNECTOR, "DVController - Dropping connection: " + name + " distributed");
        }
    }

    public void removeDatasource(String vdbName, int vdbVersion, String name, boolean distribute) {
        if (distribute && eventDistributor != null) {
            LogManager.logInfo(LogConstants.CTX_CONNECTOR, "DVController - Dropping datasource: " + name + " distributing...");
            this.eventDistributor.removeDatasource(vdbName, vdbVersion, name);
            LogManager.logInfo(LogConstants.CTX_CONNECTOR, "DVController - Dropping datasource: " + name + " distributed");
        }
    }

    public void createVirtualSchema(String vdbName, int vdbVersion, String name, boolean isDeletable, int id, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.createVirtualSchema(vdbName, vdbVersion, name, isDeletable, id);
    }

    public void dropVirtualSchema(String vdbName, int vdbVersion, int id, String name, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.dropVirtualSchema(vdbName, vdbVersion, id, name);
    }

    public void renameVirtualSchema(String vdbName, int vdbVersion, int id, String name, String oldName, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.renameVirtualSchema(vdbName, vdbVersion, id, name, oldName);
    }

    public void createTable(String vdbName, int vdbVersion, String command, boolean distribute, Action doOnInitiatorNode) {
        if (distribute && dtm != null) {
            doOnInitiatorNode.execute();
            if (eventDistributor != null)
                this.eventDistributor.createTable(vdbName, vdbVersion, command);
        }
    }

    public void dropTable(String vdbName, int vdbVersion, String command, boolean distribute, Action doOnInitiatorNode) {
        if (distribute && dtm != null) {
            doOnInitiatorNode.execute();
            if (eventDistributor != null)
                this.eventDistributor.dropTable(vdbName, vdbVersion, command);
        }    
    }

    public void selectInto(String vdbName, int vdbVersion, String command, String connectionId, long executionId, String combinedId, boolean rollbackMetadata, boolean distribute, Action doOnInitiatorNode) {
        if (distribute && dtm != null) {
            doOnInitiatorNode.execute();
            if (eventDistributor != null)
                this.eventDistributor.selectInto(vdbName, vdbVersion, command, connectionId, executionId, combinedId, rollbackMetadata);
        }    
    }

    public void removeTable(String vdbName, int vdbVersion, String schemaName, String tableName, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.removeTable(vdbName, vdbVersion, schemaName, tableName);
    }

    public void changeDWH(String vdbName, int vdbVersion, String nameInDv, String nameInSource, boolean restoreTableCounter, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.changeDWH(vdbName, vdbVersion, nameInDv, nameInSource, restoreTableCounter);
    }

    public void distributeMatTable(String vdbName, int vdbVersion, int recOptId, String matTableName, String matNameInInternalDB, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.distributeMatTable(vdbName, vdbVersion, recOptId, matTableName, matNameInInternalDB);
    }

    public void distributeGitEvent(Object observedType, Integer entityId, String operationType, boolean distribute, BiConsumer<String, Integer> beforeDistribute) {
        if (distribute && eventDistributor != null) {
            beforeDistribute.accept(observedType.toString(), entityId);
            this.eventDistributor.distributeGitEvent(observedType.toString(), entityId, operationType);
        }
    }

    public void createVdb(String name, Integer version, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.createVdb(name, version);
    }

    public void renameVdb(String name, Integer version, String oldName, Integer oldVersion, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.renameVdb(name, version, oldName, oldVersion);
    }

    public void dropVdb(String name, Integer version, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.dropVdb(name, version);
    }

    public void createQueueHandler(String name, Integer threadCount, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.createQueueHandler(name, threadCount);
    }

    public void editQueueHandler(String name, Integer threadCount, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.editQueueHandler(name, threadCount);
    }

    public void dropQueueHandler(String name, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.dropQueueHandler(name);
    }

    public void updateWorkContextMetadata(boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.updateWorkContextMetadata();
    }

    public void refreshModel(String vdbName, int vdbVersion, String name, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.refreshModel(vdbName, vdbVersion, name);
    }

    public void setDefaultOptionValue(String option, String initialValue, String encryptedValue, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.setDefaultOptionValue(option, initialValue, encryptedValue);
    }

    public void deleteSchedulerJob(Integer jobId, String jobUuid, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.deleteSchedulerJob(jobId, jobUuid);
    }

    public void cancelQuery(String sessionId, Long reqIndex, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.cancelQuery(sessionId, reqIndex);
    }

    public void callOnCompleteJobRun(Integer scheduleId, Integer id, String status, String failureReason, boolean distribute) {
        if (distribute && eventDistributor != null)
            this.eventDistributor.callOnCompleteJobRun(scheduleId, id, status, failureReason);
    }

    public void installLicense(String base64LicenseFile, boolean distribute) {
        if (distribute && eventDistributor != null)
            eventDistributor.installLicense(base64LicenseFile);
    }
}
