llmApp.directive('bsMultiselect', ['PythonService', 'ParamsHelperService', '$stateParams', 'PLUGIN_PATHS', function (PythonService, ParamsHelperService, $stateParams, PLUGIN_PATHS) {
    return {
        restrict: 'E',
        templateUrl: PLUGIN_PATHS.WEBAPP + 'bs-multiselect-template.html',
        scope: {
            config: '=',
            formName: '@',
            labelText: '@',
            configProperty: '@',
            description: '@',
            folderChoices: '=?',
            triggerParameter: '=',
            width: '@?',
            maxHeightThreshold: '@?',
            rootModel: '='
        },
        controller: ['$scope', '$timeout', '$element', function ($scope, $timeout, $element) {

            $scope.maxHeightThreshold = $scope.maxHeightThreshold || 82;
            $scope.isDropdownOpen = false;
            $scope.folders = [];
            $scope.config = $scope.config || {};
            $scope.selectedItems = [];

            $scope.showAllItems = false;
            $scope.exceedsMaxHeight = false;
            $scope.visibleItemCount = 0;
            $scope.remainingItemCount = 0;

            // --- SEARCH FUNCTIONALITY PROPERTIES ---
            $scope.searchFilter = '';
            $scope.filterActive = false;
            $scope.isSearchFocused = false;
            $scope.isDropdownHovered = false;

            let searchBlurTimeout = null;

            $scope.toggleShowAll = function () {
                $scope.showAllItems = !$scope.showAllItems;
            };

            var projectKey = $stateParams.projectKey;


            // --- SEARCH FUNCTIONALITY METHODS ---


            //Opens the dropdown with search functionality
            $scope.openDropdownWithSearch = function (clearFilter) {
                if (searchBlurTimeout) $timeout.cancel(searchBlurTimeout);

                if (clearFilter) {
                    $scope.searchFilter = '';
                }

                if (!$scope.isDropdownOpen) {
                    $scope.isDropdownOpen = true;
                    $scope.filterActive = false; // Show all items initially
                    addDocumentClickHandler();
                }
            };

            //Handles input focus for search
            $scope.handleSearchInputFocus = function () {
                if (searchBlurTimeout) {
                    $timeout.cancel(searchBlurTimeout);
                    searchBlurTimeout = null;
                }
                $scope.isSearchFocused = true;
                if (!$scope.isDropdownOpen) {
                    $scope.openDropdownWithSearch(false);
                }
            };

            //Handles search input changes
            $scope.handleSearchInputChange = function () {
                $scope.filterActive = true;

                // Automatically open dropdown if user starts typing
                if (!$scope.isDropdownOpen && $scope.searchFilter) {
                    $scope.openDropdownWithSearch(false);
                }
            };


            //Handles search input blur
            $scope.handleSearchInputBlur = function () {
                $scope.isSearchFocused = false;
                searchBlurTimeout = $timeout(() => {
                    if (!$scope.isDropdownHovered && $scope.isDropdownOpen) {
                        closeDropdownAndRevertText();
                    }
                }, 150);
            };
            

            //Clears the search filter
            $scope.clearSearchFilter = function () {
                $scope.searchFilter = '';
                $scope.filterActive = false;
                // Focus back to input after clearing
                $timeout(() => {
                    const input = angular.element(`#${$scope.configProperty}-search-input`);
                    if (input.length) {
                        input[0].focus();
                    }
                }, 0);
            };


            //Filter function for folders based on search input
            $scope.folderFilterFn = function (folder) {
                if (!$scope.filterActive) return true;
                if (!folder?.label) return false;
                if (!$scope.searchFilter) return true;
                return folder.label.toLowerCase().includes($scope.searchFilter.toLowerCase());
            };


            //Check if a type has matching folders after filtering
            $scope.typeHasMatchingFolders = function (type) {
                if (!$scope.foldersByType[type]) return false;
                return $scope.foldersByType[type].some($scope.folderFilterFn);
            };


            //Close dropdown and clean up
            const closeDropdownAndRevertText = function () {
                if (!$scope.isDropdownOpen) return;
                $scope.isDropdownOpen = false;
                $scope.isSearchFocused = false;
                removeDocumentClickHandler();
                $scope.searchFilter = '';
                $scope.filterActive = false;
            };


            //Document click handler
            var documentClickHandler = function (event) {
                if ($scope.isDropdownOpen && !$element[0].contains(event.target)) {
                    $scope.$apply(function () {
                        closeDropdownAndRevertText();
                    });
                }
            };
        

            const addDocumentClickHandler = function () {
                $timeout(() => angular.element(document).on('click', documentClickHandler), 0);
            };

            const removeDocumentClickHandler = function () {
                angular.element(document).off('click', documentClickHandler);
            };


            // Function to get visible items based on collapse state
            $scope.getVisibleItems = function () {
                if ($scope.selectedItems.length <= 3 || $scope.showAllItems) {
                    return $scope.selectedItems;
                } else {
                    return $scope.selectedItems.slice(0, 3);
                }
            };

            // Helper function to normalize data format
            function normalizeDataFormat(data) {
                // Check if data is an array (second format)
                if (Array.isArray(data)) {
                    return {
                        "General": data.map(item => ({
                            value: item.value,
                            label: item.label,
                            type: "General"
                        }))
                    };
                }

                // Check if data is an object with arrays as values (first format)
                if (data && typeof data === 'object') {
                    const normalized = {};
                    Object.keys(data).forEach(function (type) {
                        if (Array.isArray(data[type])) {
                            // Check if items in array are strings or objects
                            normalized[type] = data[type].map(item => {
                                if (typeof item === 'string') {
                                    // First format: array of strings
                                    return {
                                        value: item,
                                        label: item,
                                        type: type
                                    };
                                } else if (item && typeof item === 'object' && item.value && item.label) {
                                    // Second format: array of objects (but grouped by type)
                                    return {
                                        value: item.value,
                                        label: item.label,
                                        type: type
                                    };
                                }
                                return null;
                            }).filter(item => item !== null);
                        }
                    });
                    return normalized;
                }

                // Invalid format
                return {};
            }

            // Initialize folder data
            $scope.initFolderData = function () {

                // If folder choices are provided directly, use those
                if ($scope.folderChoices) {
                    const normalizedData = normalizeDataFormat($scope.folderChoices);
                    $scope.foldersByType = normalizedData;
                    $scope.folders = [];

                    Object.keys($scope.foldersByType).forEach(function (type) {
                        $scope.folders = $scope.folders.concat($scope.foldersByType[type]);
                    });

                    initializeSelectedFolders();

                    return;
                }

                let payload = {
                    parameterName: $scope.configProperty,
                    projectKey: projectKey,
                    rootModel : $scope.rootModel ? $scope.rootModel : $scope.config
                };


                ParamsHelperService.do(payload).then(function (data) {
                    if (data && data.choices) {
                        const normalizedData = normalizeDataFormat(data.choices);
                        $scope.foldersByType = normalizedData;
                        $scope.folders = [];

                        Object.keys($scope.foldersByType).forEach(function (type) {
                            $scope.folders = $scope.folders.concat($scope.foldersByType[type]);
                        });

                        initializeSelectedFolders();
                    } else {

                        $scope.foldersByType = {};
                        $scope.folders = [];
                    }
                }, function (error) {

                    $scope.foldersByType = {};
                    $scope.folders = [];
                });
            };

            // Helper function to set selected folders based on config
            function initializeSelectedFolders() {
                $scope.selectedItems = [];

                // If config already has folder IDs array, find and set the corresponding folder objects
                if ($scope.config[$scope.configProperty] && Array.isArray($scope.config[$scope.configProperty])) {
                    var folderIds = $scope.config[$scope.configProperty];

                    folderIds.forEach(function (folderId) {
                        var foundFolder = $scope.folders.find(function (folder) {
                            return folder.value === folderId;
                        });

                        if (foundFolder) {
                            $scope.selectedItems.push(foundFolder);
                        }
                    });

                    // If the config had values but none were found in available folders, clear it
                    if (folderIds.length > 0 && $scope.selectedItems.length === 0) {
                        $scope.config[$scope.configProperty] = [];
                    }
                }
                // Initialize the config array if it doesn't exist
                else if (!$scope.config[$scope.configProperty]) {
                    $scope.config[$scope.configProperty] = [];
                }
                checkContentHeight()
            }

            // Toggle dropdown visibility
            $scope.toggleDropdown = function (event) {
                if (event) {
                    event.stopPropagation();
                    event.preventDefault();
                }
        
                // Cancel any pending blur timeout
                if (searchBlurTimeout) {
                    $timeout.cancel(searchBlurTimeout);
                    searchBlurTimeout = null;
                }
        
                if ($scope.isDropdownOpen) {
                    closeDropdownAndRevertText();
                } else {
                    $scope.openDropdownWithSearch(false);
                    // Focus input after opening
                    $timeout(() => {
                        const input = $element[0].querySelector('input');
                        if (input) input.focus();
                    }, 0);
                }
            };
        
            // Check if a folder is selected
            $scope.isItemSelected = function (item) {
                return $scope.selectedItems.some(function (selectedItem) {
                    return selectedItem.value === item.value;
                });
            };

            // Toggle selection of a folder
            $scope.toggleItemSelection = function (item) {
                // When an item is clicked, cancel any pending blur action that would close the dropdown.
                if (searchBlurTimeout) {
                    $timeout.cancel(searchBlurTimeout);
                }

                if ($scope.isItemSelected(item)) {
                    $scope.removeItem(item);
                } else {
                    $scope.selectedItems.push(item);
                    updateConfigValue();
                }
            };

            // Remove a folder from selection
            $scope.removeItem = function (item, event) {
                if (event) {
                    event.stopPropagation();
                }

                $scope.selectedItems = $scope.selectedItems.filter(function (selectedItem) {
                    return selectedItem.value !== item.value;
                });

                updateConfigValue();
            };

            // Clear all selected items
            $scope.clearAllItems = function () {
                $scope.selectedItems = [];
                updateConfigValue();
                checkContentHeight();
            };

            // This is the core of the new logic. It calculates exactly how many items fit.
            function checkContentHeight() {
                var retries = 0;
                var maxRetries = 20; // Max attempts before giving up (only done during initialization)

                function attemptMeasurement() {
                    var container = document.getElementById($scope.configProperty + '-items-container');

                    // If container doesn't exist after retries, exit
                    if (!container) {
                        if (retries < maxRetries) {
                            retries++;
                            $timeout(attemptMeasurement, 100 * retries);
                        }
                        return;
                    }

                    var children = container.children;

                    // If no children after retries, exit
                    if (children.length === 0) {
                        if (retries < maxRetries) {
                            retries++;
                            $timeout(attemptMeasurement, 100 * retries);
                        }
                        return;
                    }

                    // First child missing dimensions? Schedule retry
                    if (children[0].offsetHeight === 0) {
                        if (retries < maxRetries) {
                            retries++;
                            $timeout(attemptMeasurement, 100 * retries);
                            return;
                        }
                    }

                    // Proceed with measurements
                    var visibleCount = children.length;
                    for (let i = 0; i < children.length; i++) {
                        const item = children[i];
                        const itemBottom = item.offsetTop + item.offsetHeight;
                        if (itemBottom > $scope.maxHeightThreshold) {
                            visibleCount = i;
                            break;
                        }
                    }

                    // Update scope properties
                    $scope.visibleItemCount = visibleCount;
                    $scope.exceedsMaxHeight = visibleCount < children.length;
                    $scope.remainingItemCount = children.length - visibleCount;

                    if (!$scope.exceedsMaxHeight) {
                        $scope.showAllItems = false;
                    }
                }

                // Initial measurement attempt
                $timeout(attemptMeasurement, 0);
            }

            // Update the config value with current selections
            function updateConfigValue() {
                $scope.config[$scope.configProperty] = $scope.selectedItems.map(function (item) {
                    return item.value;
                });
            }

            $scope.$watch('triggerParameter', function (newValue, oldValue) {

                // Only re-initialize if the value actually changed and it's not the initial watch call
                if (newValue !== oldValue) {
                    $scope.initFolderData();
                }
            }, true); // Use deep watch (true) to detect changes in object properties

            // Listen for scope destruction and reset the config property
            $scope.$on('$destroy', function () {
                angular.element(window).off('resize');
                removeDocumentClickHandler();
                if (searchBlurTimeout) $timeout.cancel(searchBlurTimeout);
            });

            // Initialize data when directive loads
            $scope.initFolderData();

            // WATCHER: This handles all subsequent changes to the selected items.
            $scope.$watch('selectedItems', function (newValue, oldValue) {
                if (newValue !== oldValue) {
                    checkContentHeight();
                }
            }, true);

        }]
    };
}]);