Automatically refresh the codebase upon merging

Each time changes are merged in Gerrit, the updated codebase is uploaded
to ChatGPT to be accessible for new assistants.

Change-Id: I968c9cf2f4aad070e2184b1d9222aca33f1bed59
Signed-off-by: Patrizio <patrizio.gelosi@amarulasolutions.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/interfaces/listener/IEventHandlerType.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/interfaces/listener/IEventHandlerType.java
new file mode 100644
index 0000000..829900f
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/interfaces/listener/IEventHandlerType.java
@@ -0,0 +1,10 @@
+package com.googlesource.gerrit.plugins.chatgpt.interfaces.listener;
+
+public interface IEventHandlerType {
+    enum PreprocessResult {
+        OK, EXIT, SWITCH_TO_PATCH_SET_CREATED
+    }
+
+    PreprocessResult preprocessEvent();
+    void processEvent() throws Exception;
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTask.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTask.java
index 872b308..2faacfb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTask.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTask.java
@@ -2,33 +2,47 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Splitter;
-import com.google.gerrit.extensions.client.ChangeKind;
 import com.google.gerrit.server.data.ChangeAttribute;
-import com.google.gerrit.server.data.PatchSetAttribute;
+import com.google.gerrit.server.events.ChangeMergedEvent;
+import com.google.gerrit.server.events.CommentAddedEvent;
+import com.google.gerrit.server.events.PatchSetCreatedEvent;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.chatgpt.PatchSetReviewer;
 import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
+import com.googlesource.gerrit.plugins.chatgpt.data.PluginDataHandlerProvider;
+import com.googlesource.gerrit.plugins.chatgpt.interfaces.listener.IEventHandlerType;
 import com.googlesource.gerrit.plugins.chatgpt.mode.common.client.api.gerrit.GerritChange;
 import com.googlesource.gerrit.plugins.chatgpt.mode.common.client.api.gerrit.GerritClient;
 import com.googlesource.gerrit.plugins.chatgpt.mode.common.model.data.ChangeSetData;
+import com.googlesource.gerrit.plugins.chatgpt.mode.stateful.client.api.git.GitRepoFiles;
 import lombok.extern.slf4j.Slf4j;
 
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
-import static com.google.gerrit.extensions.client.ChangeKind.REWORK;
-
 @Slf4j
 public class EventHandlerTask implements Runnable {
     @VisibleForTesting
     public enum Result {
         OK, NOT_SUPPORTED, FAILURE
     }
+    public enum SupportedEvents {
+        PATCH_SET_CREATED,
+        COMMENT_ADDED,
+        CHANGE_MERGED
+    }
 
-    private final static Map<String, Boolean> EVENT_COMMENT_MAP = Map.of(
-            "patchset-created", false,
-            "comment-added", true
+    public static final Map<SupportedEvents, Class<?>> EVENT_CLASS_MAP = Map.of(
+            SupportedEvents.PATCH_SET_CREATED, PatchSetCreatedEvent.class,
+            SupportedEvents.COMMENT_ADDED, CommentAddedEvent.class,
+            SupportedEvents.CHANGE_MERGED, ChangeMergedEvent.class
+    );
+
+    private static final Map<String, SupportedEvents> EVENT_TYPE_MAP = Map.of(
+        "patchset-created", SupportedEvents.PATCH_SET_CREATED,
+        "comment-added", SupportedEvents.COMMENT_ADDED,
+        "change-merged", SupportedEvents.CHANGE_MERGED
     );
 
     private final Configuration config;
@@ -36,6 +50,11 @@
     private final ChangeSetData changeSetData;
     private final GerritChange change;
     private final PatchSetReviewer reviewer;
+    private final GitRepoFiles gitRepoFiles;
+    private final PluginDataHandlerProvider pluginDataHandlerProvider;
+
+    private SupportedEvents processing_event_type;
+    private IEventHandlerType eventHandlerType;
 
     @Inject
     EventHandlerTask(
@@ -43,13 +62,17 @@
             ChangeSetData changeSetData,
             GerritChange change,
             PatchSetReviewer reviewer,
-            GerritClient gerritClient
+            GerritClient gerritClient,
+            GitRepoFiles gitRepoFiles,
+            PluginDataHandlerProvider pluginDataHandlerProvider
     ) {
         this.changeSetData = changeSetData;
         this.change = change;
         this.reviewer = reviewer;
         this.gerritClient = gerritClient;
         this.config = config;
+        this.gitRepoFiles = gitRepoFiles;
+        this.pluginDataHandlerProvider = pluginDataHandlerProvider;
     }
 
     @Override
@@ -59,13 +82,13 @@
 
     @VisibleForTesting
     public Result execute() {
-        if (!preProcessEvent(change, changeSetData)) {
+        if (!preProcessEvent()) {
             return Result.NOT_SUPPORTED;
         }
 
         try {
             log.info("Processing change: {}", change.getFullChangeId());
-            reviewer.review(change);
+            eventHandlerType.processEvent();
             log.info("Finished processing change: {}", change.getFullChangeId());
         } catch (Exception e) {
             log.error("Error while processing change: {}", change.getFullChangeId(), e);
@@ -77,41 +100,43 @@
         return Result.OK;
     }
 
-    private boolean preProcessEvent(GerritChange change, ChangeSetData changeSetData) {
+    private boolean preProcessEvent() {
         String eventType = Optional.ofNullable(change.getEventType()).orElse("");
         log.info("Event type {}", eventType);
-        if (!EVENT_COMMENT_MAP.containsKey(eventType)) {
+        processing_event_type = EVENT_TYPE_MAP.get(eventType);
+        if (processing_event_type == null) {
             return false;
         }
 
         if (!isReviewEnabled(change)) {
             return false;
         }
-        boolean isCommentEvent = EVENT_COMMENT_MAP.get(eventType);
-        if (isCommentEvent) {
-            if (!gerritClient.retrieveLastComments(change)) {
-                if (changeSetData.getForcedReview() || changeSetData.getForceDisplaySystemMessage()) {
-                    isCommentEvent = false;
-                } else {
-                    log.info("No comments found for review");
+
+        while (true) {
+            eventHandlerType = getEventHandlerType();
+            switch (eventHandlerType.preprocessEvent()) {
+                case EXIT -> {
                     return false;
                 }
+                case SWITCH_TO_PATCH_SET_CREATED -> {
+                    processing_event_type = SupportedEvents.PATCH_SET_CREATED;
+                    continue;
+                }
             }
-        } else {
-            if (!isPatchSetReviewEnabled(change)) {
-                log.debug("Patch Set review disabled");
-                return false;
-            }
-        }
-        log.debug("Flag `isCommentEvent` set to {}", isCommentEvent);
-        change.setIsCommentEvent(isCommentEvent);
-        if (!isCommentEvent) {
-            gerritClient.retrievePatchSetInfo(change);
+            break;
         }
 
         return true;
     }
 
+    private IEventHandlerType getEventHandlerType() {
+        return switch (processing_event_type) {
+            case PATCH_SET_CREATED -> new EventHandlerTypePatchSetReview(config, changeSetData, change, reviewer, gerritClient);
+            case COMMENT_ADDED -> new EventHandlerTypeCommentAdded(changeSetData, change, reviewer, gerritClient);
+            case CHANGE_MERGED -> new EventHandlerTypeChangeMerged(config, changeSetData, change, gitRepoFiles, pluginDataHandlerProvider);
+        };
+    }
+
     private boolean isReviewEnabled(GerritChange change) {
         List<String> enabledProjects = Splitter.on(",").omitEmptyStrings()
                 .splitToList(config.getEnabledProjects());
@@ -132,34 +157,6 @@
         return true;
     }
 
-    private boolean isPatchSetReviewEnabled(GerritChange change) {
-        if (!config.getGptReviewPatchSet()) {
-            log.debug("Disabled review function for created or updated PatchSets.");
-            return false;
-        }
-        Optional<PatchSetAttribute> patchSetAttributeOptional = change.getPatchSetAttribute();
-        if (patchSetAttributeOptional.isEmpty()) {
-            log.info("PatchSetAttribute event properties not retrieved");
-            return false;
-        }
-        PatchSetAttribute patchSetAttribute = patchSetAttributeOptional.get();
-        ChangeKind patchSetEventKind = patchSetAttribute.kind;
-        if (patchSetEventKind != REWORK) {
-            log.debug("Change kind '{}' not processed", patchSetEventKind);
-            return false;
-        }
-        String authorUsername = patchSetAttribute.author.username;
-        if (gerritClient.isDisabledUser(authorUsername)) {
-            log.info("Review of PatchSets from user '{}' is disabled.", authorUsername);
-            return false;
-        }
-        if (gerritClient.isWorkInProgress(change)) {
-            log.debug("Skipping Patch Set processing due to its WIP status.");
-            return false;
-        }
-        return true;
-    }
-
     private Optional<String> getTopic(GerritChange change) {
         try {
             ChangeAttribute changeAttribute = change.getPatchSetEvent().change.get();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTypeChangeMerged.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTypeChangeMerged.java
new file mode 100644
index 0000000..6d033ba
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTypeChangeMerged.java
@@ -0,0 +1,52 @@
+package com.googlesource.gerrit.plugins.chatgpt.listener;
+
+import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
+import com.googlesource.gerrit.plugins.chatgpt.data.PluginDataHandlerProvider;
+import com.googlesource.gerrit.plugins.chatgpt.interfaces.listener.IEventHandlerType;
+import com.googlesource.gerrit.plugins.chatgpt.mode.common.client.api.gerrit.GerritChange;
+import com.googlesource.gerrit.plugins.chatgpt.mode.common.model.data.ChangeSetData;
+import com.googlesource.gerrit.plugins.chatgpt.mode.stateful.client.api.chatgpt.ChatGptAssistantBase;
+import com.googlesource.gerrit.plugins.chatgpt.mode.stateful.client.api.chatgpt.ChatGptAssistantReview;
+import com.googlesource.gerrit.plugins.chatgpt.mode.stateful.client.api.git.GitRepoFiles;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class EventHandlerTypeChangeMerged implements IEventHandlerType {
+    private final Configuration config;
+    private final ChangeSetData changeSetData;
+    private final GerritChange change;
+    private final GitRepoFiles gitRepoFiles;
+    private final PluginDataHandlerProvider pluginDataHandlerProvider;
+
+    EventHandlerTypeChangeMerged(
+            Configuration config,
+            ChangeSetData changeSetData,
+            GerritChange change,
+            GitRepoFiles gitRepoFiles,
+            PluginDataHandlerProvider pluginDataHandlerProvider
+    ) {
+        this.config = config;
+        this.changeSetData = changeSetData;
+        this.change = change;
+        this.gitRepoFiles = gitRepoFiles;
+        this.pluginDataHandlerProvider = pluginDataHandlerProvider;
+    }
+
+    @Override
+    public PreprocessResult preprocessEvent() {
+        return PreprocessResult.OK;
+    }
+
+    @Override
+    public void processEvent() {
+        ChatGptAssistantBase chatGptAssistant = new ChatGptAssistantBase(
+                config,
+                changeSetData,
+                change,
+                gitRepoFiles,
+                pluginDataHandlerProvider
+        );
+        chatGptAssistant.flushAssistantIds();
+        chatGptAssistant.createVectorStore();
+    }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTypeCommentAdded.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTypeCommentAdded.java
new file mode 100644
index 0000000..ec730b7
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTypeCommentAdded.java
@@ -0,0 +1,49 @@
+package com.googlesource.gerrit.plugins.chatgpt.listener;
+
+import com.googlesource.gerrit.plugins.chatgpt.PatchSetReviewer;
+import com.googlesource.gerrit.plugins.chatgpt.interfaces.listener.IEventHandlerType;
+import com.googlesource.gerrit.plugins.chatgpt.mode.common.client.api.gerrit.GerritChange;
+import com.googlesource.gerrit.plugins.chatgpt.mode.common.client.api.gerrit.GerritClient;
+import com.googlesource.gerrit.plugins.chatgpt.mode.common.model.data.ChangeSetData;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class EventHandlerTypeCommentAdded implements IEventHandlerType {
+    private final ChangeSetData changeSetData;
+    private final GerritChange change;
+    private final PatchSetReviewer reviewer;
+    private final GerritClient gerritClient;
+
+    EventHandlerTypeCommentAdded(
+            ChangeSetData changeSetData,
+            GerritChange change,
+            PatchSetReviewer reviewer,
+            GerritClient gerritClient
+    ) {
+        this.changeSetData = changeSetData;
+        this.change = change;
+        this.reviewer = reviewer;
+        this.gerritClient = gerritClient;
+
+    }
+
+    @Override
+    public PreprocessResult preprocessEvent() {
+        if (!gerritClient.retrieveLastComments(change)) {
+            if (changeSetData.getForcedReview()) {
+                return PreprocessResult.SWITCH_TO_PATCH_SET_CREATED;
+            } else {
+                log.info("No comments found for review");
+                return PreprocessResult.EXIT;
+            }
+        }
+        change.setIsCommentEvent(true);
+
+        return PreprocessResult.OK;
+    }
+
+    @Override
+    public void processEvent() throws Exception {
+        reviewer.review(change);
+    }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTypePatchSetReview.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTypePatchSetReview.java
new file mode 100644
index 0000000..d372f9e
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/EventHandlerTypePatchSetReview.java
@@ -0,0 +1,84 @@
+package com.googlesource.gerrit.plugins.chatgpt.listener;
+
+import com.google.gerrit.extensions.client.ChangeKind;
+import com.google.gerrit.server.data.PatchSetAttribute;
+import com.googlesource.gerrit.plugins.chatgpt.PatchSetReviewer;
+import com.googlesource.gerrit.plugins.chatgpt.config.Configuration;
+import com.googlesource.gerrit.plugins.chatgpt.interfaces.listener.IEventHandlerType;
+import com.googlesource.gerrit.plugins.chatgpt.mode.common.client.api.gerrit.GerritChange;
+import com.googlesource.gerrit.plugins.chatgpt.mode.common.client.api.gerrit.GerritClient;
+import com.googlesource.gerrit.plugins.chatgpt.mode.common.model.data.ChangeSetData;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Optional;
+
+import static com.google.gerrit.extensions.client.ChangeKind.REWORK;
+
+@Slf4j
+public class EventHandlerTypePatchSetReview implements IEventHandlerType {
+    private final Configuration config;
+    private final ChangeSetData changeSetData;
+    private final GerritChange change;
+    private final PatchSetReviewer reviewer;
+    private final GerritClient gerritClient;
+
+    EventHandlerTypePatchSetReview(
+            Configuration config,
+            ChangeSetData changeSetData,
+            GerritChange change,
+            PatchSetReviewer reviewer,
+            GerritClient gerritClient
+    ) {
+        this.config = config;
+        this.changeSetData = changeSetData;
+        this.change = change;
+        this.reviewer = reviewer;
+        this.gerritClient = gerritClient;
+    }
+
+    @Override
+    public PreprocessResult preprocessEvent() {
+        if (!isPatchSetReviewEnabled(change)) {
+            log.debug("Patch Set review disabled");
+            return PreprocessResult.EXIT;
+        }
+        gerritClient.retrievePatchSetInfo(change);
+
+        return PreprocessResult.OK;
+    }
+
+    @Override
+    public void processEvent() throws Exception {
+        reviewer.review(change);
+    }
+
+    private boolean isPatchSetReviewEnabled(GerritChange change) {
+        if (!config.getGptReviewPatchSet()) {
+            log.debug("Disabled review function for created or updated PatchSets.");
+            return false;
+        }
+        Optional<PatchSetAttribute> patchSetAttributeOptional = change.getPatchSetAttribute();
+        if (patchSetAttributeOptional.isEmpty()) {
+            log.info("PatchSetAttribute event properties not retrieved");
+            return false;
+        }
+        PatchSetAttribute patchSetAttribute = patchSetAttributeOptional.get();
+        ChangeKind patchSetEventKind = patchSetAttribute.kind;
+        // The only Change kind that automatically triggers the review is REWORK. If review is forced via command, this
+        // condition is bypassed
+        if (patchSetEventKind != REWORK && !changeSetData.getForcedReview()) {
+            log.debug("Change kind '{}' not processed", patchSetEventKind);
+            return false;
+        }
+        String authorUsername = patchSetAttribute.author.username;
+        if (gerritClient.isDisabledUser(authorUsername)) {
+            log.info("Review of PatchSets from user '{}' is disabled.", authorUsername);
+            return false;
+        }
+        if (gerritClient.isWorkInProgress(change)) {
+            log.debug("Skipping Patch Set processing due to its WIP status.");
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/GerritListener.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/GerritListener.java
index 2e60049..82d4cbd 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/GerritListener.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/listener/GerritListener.java
@@ -4,11 +4,7 @@
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.config.GerritInstanceId;
-import com.google.gerrit.server.events.PatchSetEvent;
-import com.google.gerrit.server.events.Event;
-import com.google.gerrit.server.events.EventListener;
-import com.google.gerrit.server.events.CommentAddedEvent;
-import com.google.gerrit.server.events.PatchSetCreatedEvent;
+import com.google.gerrit.server.events.*;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.chatgpt.config.ConfigCreator;
@@ -17,6 +13,8 @@
 
 import java.util.Objects;
 
+import static com.googlesource.gerrit.plugins.chatgpt.listener.EventHandlerTask.EVENT_CLASS_MAP;
+
 @Slf4j
 public class GerritListener implements EventListener {
     private final String myInstanceId;
@@ -40,8 +38,8 @@
             log.debug("Ignore event from another instance");
             return;
         }
-        if (!(event instanceof CommentAddedEvent || event instanceof PatchSetCreatedEvent)) {
-            log.debug("The event is not a PatchSetCreatedEvent, it is: {}", event);
+        if (!EVENT_CLASS_MAP.containsValue(event.getClass())) {
+            log.debug("The event {} is not managed by the plugin", event);
             return;
         }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/commands/ClientCommands.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/commands/ClientCommands.java
index 5489ff7..7e8ce7c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/commands/ClientCommands.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/commands/ClientCommands.java
@@ -127,7 +127,6 @@
             else if (command == COMMAND_SET.CONFIGURE) {
                 if (config.getEnableMessageDebugging()) {
                     changeSetData.setHideChatGptReview(true);
-                    changeSetData.setForceDisplaySystemMessage(true);
                     dynamicConfiguration.updateConfiguration(modifiedDynamicConfig, shouldResetDynamicConfig);
                 }
                 else {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/model/data/ChangeSetData.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/model/data/ChangeSetData.java
index ab5cd23..083d9f9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/model/data/ChangeSetData.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/model/data/ChangeSetData.java
@@ -24,7 +24,6 @@
     // Command variables
     private Boolean forcedReview = false;
     private Boolean forcedReviewLastPatchSet = false;
-    private Boolean forceDisplaySystemMessage = false;
     private Boolean replyFilterEnabled = true;
     private Boolean debugReviewMode = false;
     private Boolean hideChatGptReview = false;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantBase.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantBase.java
index 9b26bac..ba4f1c8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantBase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantBase.java
@@ -27,6 +27,9 @@
 
 @Slf4j
 public class ChatGptAssistantBase extends ClientBase {
+    protected static final String KEY_REVIEW_ASSISTANT_ID = "reviewAssistantId";
+    protected static final String KEY_REQUESTS_ASSISTANT_ID = "requestsAssistantId";
+
     @Getter
     protected String keyAssistantId;
 
@@ -54,8 +57,7 @@
         String assistantId = projectDataHandler.getValue(keyAssistantId);
         if (assistantId == null || config.getForceCreateAssistant()) {
             log.debug("Setup Assistant for project {}", change.getProjectNameKey());
-            String fileId = uploadRepoFiles();
-            String vectorStoreId = createVectorStore(fileId);
+            String vectorStoreId = createVectorStore();
             assistantId = createAssistant(vectorStoreId);
             projectDataHandler.setValue(keyAssistantId, assistantId);
             log.info("Project assistant created with ID: {}", assistantId);
@@ -65,6 +67,28 @@
         }
     }
 
+    public String createVectorStore() {
+        String vectorStoreId = projectDataHandler.getValue(KEY_VECTOR_STORE_ID);
+        if (vectorStoreId == null) {
+            String fileId = uploadRepoFiles();
+            ChatGptVectorStore vectorStore = new ChatGptVectorStore(fileId, config, change);
+            ChatGptResponse createVectorStoreResponse = vectorStore.createVectorStore();
+            vectorStoreId = createVectorStoreResponse.getId();
+            projectDataHandler.setValue(KEY_VECTOR_STORE_ID, vectorStoreId);
+            log.info("Vector Store created with ID: {}", vectorStoreId);
+        }
+        else {
+            log.info("Vector Store found for the project. Vector Store ID: {}", vectorStoreId);
+        }
+        return vectorStoreId;
+    }
+
+    public void flushAssistantIds() {
+        projectDataHandler.removeValue(KEY_VECTOR_STORE_ID);
+        projectDataHandler.removeValue(KEY_REVIEW_ASSISTANT_ID);
+        projectDataHandler.removeValue(KEY_REQUESTS_ASSISTANT_ID);
+    }
+
     private String uploadRepoFiles() {
         String repoFiles = gitRepoFiles.getGitRepoFiles(config, change);
         Path repoPath = createTempFileWithContent(sanitizeFilename(change.getProjectName()), ".json", repoFiles);
@@ -74,21 +98,6 @@
         return chatGptFilesResponse.getId();
     }
 
-    private String createVectorStore(String fileId) {
-        String vectorStoreId = projectDataHandler.getValue(KEY_VECTOR_STORE_ID);
-        if (vectorStoreId == null) {
-            ChatGptVectorStore vectorStore = new ChatGptVectorStore(fileId, config, change);
-            ChatGptResponse createVectorStoreResponse = vectorStore.createVectorStore();
-            vectorStoreId = createVectorStoreResponse.getId();
-            projectDataHandler.setValue(KEY_VECTOR_STORE_ID, vectorStoreId);
-            log.info("Vector store created with ID: {}", vectorStoreId);
-        }
-        else {
-            log.info("Vector store found for the project. Assistant ID: {}", vectorStoreId);
-        }
-        return vectorStoreId;
-    }
-
     private String createAssistant(String vectorStoreId) {
         Request request = createRequest(vectorStoreId);
         log.debug("ChatGPT Create Assistant request: {}", request);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantRequests.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantRequests.java
index c78d349..f2f0b5a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantRequests.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantRequests.java
@@ -9,8 +9,6 @@
 
 @Slf4j
 public class ChatGptAssistantRequests extends ChatGptAssistantBase {
-    public static final String KEY_REQUESTS_ASSISTANT_ID = "requestsAssistantId";
-
     public ChatGptAssistantRequests(
             Configuration config,
             ChangeSetData changeSetData,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantReview.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantReview.java
index 26176f7..abc8ac9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantReview.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptAssistantReview.java
@@ -9,8 +9,6 @@
 
 @Slf4j
 public class ChatGptAssistantReview extends ChatGptAssistantBase {
-    public static final String KEY_REVIEW_ASSISTANT_ID = "reviewAssistantId";
-
     public ChatGptAssistantReview(
             Configuration config,
             ChangeSetData changeSetData,
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTest.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTest.java
index 9dccaae..f47db19 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTest.java
@@ -7,6 +7,8 @@
 import com.google.gerrit.extensions.common.DiffInfo;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.restapi.RestApiException;
+import com.googlesource.gerrit.plugins.chatgpt.data.PluginDataHandler;
+import com.googlesource.gerrit.plugins.chatgpt.data.PluginDataHandlerProvider;
 import com.googlesource.gerrit.plugins.chatgpt.mode.common.model.api.chatgpt.ChatGptResponseContent;
 import com.googlesource.gerrit.plugins.chatgpt.mode.stateful.client.api.UriResourceLocatorStateful;
 import com.googlesource.gerrit.plugins.chatgpt.mode.stateful.client.prompt.ChatGptPromptStateful;
@@ -25,8 +27,9 @@
 import java.io.ByteArrayInputStream;
 import java.net.URI;
 
-
+import static com.googlesource.gerrit.plugins.chatgpt.listener.EventHandlerTask.SupportedEvents;
 import static com.googlesource.gerrit.plugins.chatgpt.mode.stateful.client.api.chatgpt.ChatGptRun.COMPLETED_STATUS;
+import static com.googlesource.gerrit.plugins.chatgpt.mode.stateful.client.api.chatgpt.ChatGptVectorStore.KEY_VECTOR_STORE_ID;
 import static com.googlesource.gerrit.plugins.chatgpt.settings.Settings.GERRIT_PATCH_SET_FILENAME;
 import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.getGson;
 import static java.net.HttpURLConnection.HTTP_OK;
@@ -46,6 +49,7 @@
     private String formattedPatchContent;
     private ChatGptPromptStateful chatGptPromptStateful;
     private String requestContent;
+    private PluginDataHandler projectHandler;
 
     public ChatGptReviewStatefulTest() {
         MockitoAnnotations.openMocks(this);
@@ -58,8 +62,11 @@
         when(globalConfig.getString(Mockito.eq("gptMode"), Mockito.anyString()))
                 .thenReturn(MODES.stateful.name());
 
+        setupPluginData();
+        PluginDataHandlerProvider provider = new PluginDataHandlerProvider(mockPluginDataPath, getGerritChange());
+        projectHandler = provider.getProjectScope();
         // Mock the pluginDataHandlerProvider to return the mocked project pluginDataHandler
-        when(pluginDataHandlerProvider.getProjectScope()).thenReturn(pluginDataHandler);
+        when(pluginDataHandlerProvider.getProjectScope()).thenReturn(projectHandler);
     }
 
     protected void initTest() {
@@ -184,7 +191,7 @@
 
         String reviewUserPrompt = chatGptPromptStateful.getDefaultGptThreadReviewMessage(formattedPatchContent);
 
-        handleEventBasedOnType(false);
+        handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
 
         ArgumentCaptor<ReviewInput> captor = testRequestSent();
         Assert.assertEquals(reviewUserPrompt, requestContent);
@@ -205,7 +212,7 @@
                         .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
                         .withBodyFile("chatGptResponseRequestStateful.json")));
 
-        handleEventBasedOnType(true);
+        handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
 
         ArgumentCaptor<ReviewInput> captor = testRequestSent();
         Assert.assertEquals(promptTagComments, requestContent);
@@ -231,7 +238,7 @@
                         .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
                         .withBodyFile("chatGptResponseThreadMessageText.json")));
 
-        handleEventBasedOnType(true);
+        handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
 
         ArgumentCaptor<ReviewInput> captor = testRequestSent();
         Assert.assertEquals(promptTagComments, requestContent);
@@ -257,10 +264,18 @@
                         .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
                         .withBodyFile("chatGptResponseThreadMessageJson.json")));
 
-        handleEventBasedOnType(true);
+        handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
 
         ArgumentCaptor<ReviewInput> captor = testRequestSent();
         Assert.assertEquals(promptTagComments, requestContent);
         Assert.assertEquals(reviewMessageCommitMessage, getCapturedMessage(captor, GERRIT_PATCH_SET_FILENAME));
     }
+
+    @Test
+    public void gerritMergedCommits() {
+        projectHandler.removeValue(KEY_VECTOR_STORE_ID);
+        handleEventBasedOnType(SupportedEvents.CHANGE_MERGED);
+
+        Assert.assertEquals(CHAT_GPT_VECTOR_ID, projectHandler.getValue(KEY_VECTOR_STORE_ID));
+    }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatelessTest.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatelessTest.java
index 101eb61..1adb0c7 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatelessTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatelessTest.java
@@ -28,6 +28,7 @@
 import java.util.Arrays;
 import java.util.Map;
 
+import static com.googlesource.gerrit.plugins.chatgpt.listener.EventHandlerTask.SupportedEvents;
 import static com.googlesource.gerrit.plugins.chatgpt.utils.TextUtils.joinWithNewLine;
 import static java.net.HttpURLConnection.HTTP_OK;
 import static org.mockito.Mockito.mock;
@@ -124,7 +125,7 @@
         String reviewUserPrompt = getReviewUserPrompt();
         chatGptPromptStateless.setCommentEvent(false);
 
-        handleEventBasedOnType(false);
+        handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
 
         ArgumentCaptor<ReviewInput> captor = testRequestSent();
         String systemPrompt = prompts.get(0).getAsJsonObject().get("content").getAsString();
@@ -152,7 +153,7 @@
                         .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
                         .withBodyFile("chatGptResponseReview.json")));
 
-        handleEventBasedOnType(false);
+        handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED);
 
         ArgumentCaptor<ReviewInput> captor = testRequestSent();
         String userPrompt = prompts.get(1).getAsJsonObject().get("content").getAsString();
@@ -167,7 +168,7 @@
         when(globalConfig.getString(Mockito.eq("disabledGroups"), Mockito.anyString()))
                 .thenReturn(GERRIT_USER_GROUP);
 
-        Assert.assertEquals(EventHandlerTask.Result.NOT_SUPPORTED, handleEventBasedOnType(false));
+        Assert.assertEquals(EventHandlerTask.Result.NOT_SUPPORTED, handleEventBasedOnType(SupportedEvents.PATCH_SET_CREATED));
     }
 
     @Test
@@ -181,7 +182,7 @@
                         .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())
                         .withBodyFile("chatGptResponseRequestStateless.json")));
 
-        handleEventBasedOnType(true);
+        handleEventBasedOnType(SupportedEvents.COMMENT_ADDED);
         int commentPropertiesSize = gerritClient.getClientData(getGerritChange()).getCommentProperties().size();
 
         String commentUserPrompt = joinWithNewLine(Arrays.asList(
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTestBase.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTestBase.java
index 6a09527..f664bf9 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTestBase.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTestBase.java
@@ -17,10 +17,7 @@
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.data.AccountAttribute;
 import com.google.gerrit.server.data.PatchSetAttribute;
-import com.google.gerrit.server.events.CommentAddedEvent;
-import com.google.gerrit.server.events.Event;
-import com.google.gerrit.server.events.PatchSetCreatedEvent;
-import com.google.gerrit.server.events.PatchSetEvent;
+import com.google.gerrit.server.events.*;
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.gson.Gson;
 import com.google.gson.JsonObject;
@@ -67,6 +64,7 @@
 import java.util.function.Consumer;
 
 import static com.google.gerrit.extensions.client.ChangeKind.REWORK;
+import static com.googlesource.gerrit.plugins.chatgpt.listener.EventHandlerTask.EVENT_CLASS_MAP;
 import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.getGson;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -272,9 +270,9 @@
         }
     }
 
-    protected EventHandlerTask.Result handleEventBasedOnType(boolean isCommentEvent) {
-        Consumer<Event> typeSpecificSetup = getTypeSpecificSetup(isCommentEvent);
-        Event event = isCommentEvent ? mock(CommentAddedEvent.class) : mock(PatchSetCreatedEvent.class);
+    protected EventHandlerTask.Result handleEventBasedOnType(EventHandlerTask.SupportedEvents triggeredEvent) {
+        Consumer<Event> typeSpecificSetup = getTypeSpecificSetup(triggeredEvent);
+        Event event = getMockedEvent(triggeredEvent);
         setupCommonEventMocks((PatchSetEvent) event); // Apply common mock configurations
         typeSpecificSetup.accept(event);
 
@@ -295,7 +293,7 @@
     }
 
     protected ArgumentCaptor<ReviewInput> testRequestSent() throws RestApiException {
-        ArgumentCaptor<ReviewInput> reviewInputCaptor = ArgumentCaptor.forClass(ReviewInput.class); 
+        ArgumentCaptor<ReviewInput> reviewInputCaptor = ArgumentCaptor.forClass(ReviewInput.class);
         verify(revisionApiMock).review(reviewInputCaptor.capture());
         gptRequestBody = getGson().fromJson(patchSetReviewer.getChatGptClient().getRequestBody(), JsonObject.class);
         return reviewInputCaptor;
@@ -349,25 +347,29 @@
     }
 
     @NonNull
-    private Consumer<Event> getTypeSpecificSetup(boolean isCommentEvent) {
-        Consumer<Event> typeSpecificSetup;
-
-        if (isCommentEvent) {
-            typeSpecificSetup = event -> {
+    private Consumer<Event> getTypeSpecificSetup(EventHandlerTask.SupportedEvents triggeredEvent) {
+        return switch (triggeredEvent) {
+            case COMMENT_ADDED -> event -> {
                 CommentAddedEvent commentEvent = (CommentAddedEvent) event;
                 commentEvent.author = this::createTestAccountAttribute;
                 commentEvent.patchSet = this::createPatchSetAttribute;
                 commentEvent.eventCreatedOn = TEST_TIMESTAMP;
                 when(commentEvent.getType()).thenReturn("comment-added");
             };
-        } else {
-            typeSpecificSetup = event -> {
+            case PATCH_SET_CREATED -> event -> {
                 PatchSetCreatedEvent patchEvent = (PatchSetCreatedEvent) event;
                 patchEvent.patchSet = this::createPatchSetAttribute;
                 when(patchEvent.getType()).thenReturn("patchset-created");
             };
-        }
-        return typeSpecificSetup;
+            case CHANGE_MERGED -> event -> {
+                ChangeMergedEvent mergedEvent = (ChangeMergedEvent) event;
+                when(mergedEvent.getType()).thenReturn("change-merged");
+            };
+        };
+    }
+
+    private Event getMockedEvent(EventHandlerTask.SupportedEvents triggeredEvent) {
+        return (Event) mock(EVENT_CLASS_MAP.get(triggeredEvent));
     }
 
     private void setupCommonEventMocks(PatchSetEvent event) {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptTestBase.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptTestBase.java
index b3488b4..d04186d 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptTestBase.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptTestBase.java
@@ -4,12 +4,35 @@
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.Project;
 import com.googlesource.gerrit.plugins.chatgpt.mode.common.client.api.gerrit.GerritChange;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mock;
+
+import java.nio.file.Path;
+
+import static org.mockito.Mockito.when;
 
 public class ChatGptTestBase {
     protected static final Project.NameKey PROJECT_NAME = Project.NameKey.parse("myProject");
     protected static final Change.Key CHANGE_ID = Change.Key.parse("myChangeId");
     protected static final BranchNameKey BRANCH_NAME = BranchNameKey.create(PROJECT_NAME, "myBranchName");
 
+    @Rule
+    public TemporaryFolder tempFolder = new TemporaryFolder();
+
+    @Mock
+    protected Path mockPluginDataPath;
+
+    protected Path realPluginDataPath;
+
+    protected void setupPluginData() {
+        realPluginDataPath = tempFolder.getRoot().toPath().resolve("global.data");
+        Path realProjectDataPath = tempFolder.getRoot().toPath().resolve(PROJECT_NAME + ".data");
+
+        // Mock the PluginData annotation project behavior
+        when(mockPluginDataPath.resolve(PROJECT_NAME + ".data")).thenReturn(realProjectDataPath);
+    }
+
     protected GerritChange getGerritChange() {
         return new GerritChange(ChatGptTestBase.PROJECT_NAME, ChatGptTestBase.BRANCH_NAME, ChatGptTestBase.CHANGE_ID);
     }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/PluginDataTest.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/PluginDataTest.java
index d3b687a..f6e6f83 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/PluginDataTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/PluginDataTest.java
@@ -1,40 +1,26 @@
 package com.googlesource.gerrit.plugins.chatgpt;
 
 import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.when;
 
 import java.nio.file.Files;
-import java.nio.file.Path;
 
 import com.googlesource.gerrit.plugins.chatgpt.data.PluginDataHandler;
 import com.googlesource.gerrit.plugins.chatgpt.data.PluginDataHandlerProvider;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
 @RunWith(MockitoJUnitRunner.class)
 public class PluginDataTest extends ChatGptTestBase {
-    @Rule
-    public TemporaryFolder tempFolder = new TemporaryFolder();
-
-    @Mock
-    private Path mockPluginDataPath;
-
-    private Path realPluginDataPath;
 
     @Before
     public void setUp() {
-        // Setup temporary folder for tests
-        realPluginDataPath = tempFolder.getRoot().toPath().resolve("global.data");
-        Path realProjectDataPath = tempFolder.getRoot().toPath().resolve(PROJECT_NAME + ".data");
+        setupPluginData();
 
-        // Mock the PluginData annotation behavior
+        // Mock the PluginData annotation global behavior
         when(mockPluginDataPath.resolve("global.data")).thenReturn(realPluginDataPath);
-        when(mockPluginDataPath.resolve(PROJECT_NAME + ".data")).thenReturn(realProjectDataPath);
     }
 
     @Test