llmApp.directive('bsSelect', ['PythonService', 'ParamsHelperService', 'DescParamsService', 'customFileUploaderService', 'PLUGIN_PATHS', '$stateParams', function (PythonService, ParamsHelperService, DescParamsService, customFileUploaderService, PLUGIN_PATHS, $stateParams) {
    return {
        restrict: 'E',
        templateUrl: PLUGIN_PATHS.WEBAPP + 'bs-select-template.html',
        scope: {
            config: '=',
            formName: '=',
            labelText: '@',
            fieldRequired: '=',
            configProperty: '@',
            description: '@',
            defaultValue: '@',
            folderChoices: '=?',
            customMargin: '@?',
            isLanguageMappingsArray: '=?',
            getChoicesFromPython: '@',
            writePermission: '@',
            triggerParameter: '=',
            validationMessage: '@',
            rootModel: '=',
            isFolder: '@'
        },
        link: function (scope) {
            scope.config = scope.config || {};
        },
        controller: ['$scope', '$timeout', '$element', function ($scope, $timeout, $element) {

            // --- 1. State Management Variables (Names match template) ---

            // UI State
            $scope.isFolderDropdownOpen = false;
            $scope.isDropdownHovered = false;
            $scope.isFolderFocused = false; // Used in ng-focus and ng-class
            $scope.isCreatingFolder = false;

            // Data Models
            $scope.folders = [];
            $scope.folderFilter = '';
            $scope.selectedFolder = null;
            $scope.folderCreationError = '';
            //Flag to control when filtering is applied (starts false to show all on open)
            $scope.filterActive = false;

            // Internal State
            let folderBlurTimeout = null;

            // Get project key from state params
            var projectKey = $stateParams.projectKey;


            // --- 2. Private Helper Functions ---

            const processFolderChoices = function (choices) {
                if (!Array.isArray(choices)) return [];
                if (
                    $scope.configProperty === 'custom_theme_name' &&
                    $scope.config
                ) {
                    //Add a symbolic default value
                    const defaultFolder = {value: "", label: "Default customization"};

                    // Check if DEFAULT folder exists in the current folder list
                    const defaultFolderExists = choices.some(f =>
                        f.value === defaultFolder.value &&
                        f.label === defaultFolder.label // Match both properties
                    );

                    // Add DEFAULT folder to list if missing (immutable update)
                    if (!defaultFolderExists) {
                        choices = [...choices, defaultFolder];
                    }

                    // Separate the default item from the rest
                    const defaultItem = choices.find(choice => choice.label === "Default customization");
                    const otherItems = choices.filter(choice => choice.label !== "Default customization");

                    // Sort other items alphabetically by label
                    otherItems.sort((a, b) => a.label.localeCompare(b.label));

                    // Return with default first, then sorted items
                    return defaultItem ? [defaultItem, ...otherItems] : otherItems;
                }
                return choices.filter(choice => choice.value !== "" && choice.label.toLowerCase() !== "none");
            };

            const transformLanguageMappingsToFolderChoices = function (languageMappings) {
                if (!Array.isArray(languageMappings)) return [];
                return languageMappings
                    .filter(mapping => mapping && mapping.to)
                    .map(mapping => ({label: mapping.to, value: mapping.to}));
            };

            const syncSelectionFromConfig = function () {
                if (
                    $scope.configProperty === 'custom_theme_name' &&
                    $scope.config &&
                    ($scope.config.custom_theme_name === 'Default customization' || !$scope.config.custom_theme_name)
                ) {
                    const defaultFolder = {value: "", label: "Default customization"};

                    // Check if DEFAULT folder exists in the current folder list
                    const defaultFolderExists = $scope.folders.some(f =>
                        f.value === defaultFolder.value &&
                        f.label === defaultFolder.label // Match both properties
                    );

                    // Add DEFAULT folder to list if missing (immutable update)
                    if (!defaultFolderExists) {
                        $scope.folders = [...$scope.folders, defaultFolder];
                    }

                    // Select the DEFAULT folder
                    if (!$scope.selectedFolder || $scope.selectedFolder.value !== defaultFolder.value) {
                        $scope.selectFolder(defaultFolder);
                    }

                    return; // Prevent normal selection logic
                }

                const folderId = $scope.config[$scope.configProperty];
                if (folderId) {
                    const foundFolder = $scope.folders.find(f => f.value === folderId);
                    if (foundFolder) {
                        $scope.selectFolder(foundFolder);
                    } else if ($scope.defaultValue) {
                        $scope.selectFolder({label: $scope.defaultValue, value: $scope.defaultValue});
                    } else {
                        $scope.selectFolder(null);
                    }
                } else {
                    $scope.selectFolder(null);
                }
            };

            const setAndProcessFolders = function (rawChoices) {
                let processedChoices;
                if ($scope.isLanguageMappingsArray) {
                    processedChoices = transformLanguageMappingsToFolderChoices($scope.isLanguageMappingsArray);
                } else {
                    processedChoices = processFolderChoices(rawChoices);
                }
                // Initialize hover states
                $scope.folderHoverStates = processedChoices.reduce((acc, folder) => {
                    acc[folder.value] = false;
                    return acc;
                }, {});

                $scope.folders = processedChoices;
                syncSelectionFromConfig();
            };

            const handleDocumentClick = function (event) {
                // If the dropdown is open and the click is outside the component element
                if ($scope.isFolderDropdownOpen && !$element[0].contains(event.target)) {
                    $scope.$apply(() => closeFolderDropdownAndRevertText());
                }
            };

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

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

            /**
             * Closes dropdown and reverts input text to match selection. Called on blur or outside click.
             */
            const closeFolderDropdownAndRevertText = function () {
                if (!$scope.isFolderDropdownOpen) return;
                $scope.isFolderDropdownOpen = false;
                $scope.isFolderFocused = false;
                removeDocumentClickHandler();
                $scope.folderFilter = $scope.selectedFolder ? $scope.selectedFolder.label : '';
                //Reset filterActive on close
                $scope.filterActive = false;
            };


            // --- 3. Scope Functions (Bound to the Template) ---

            /**
             * Opens the dropdown. Called by ng-focus on input and ng-click on arrow.
             * @param {Event} [$event] - The DOM event.
             * @param {boolean} [clearFilter=false] - If true, clears the input text.
             */
            $scope.openFolderDropdown = function (clearFilter) {
                if (folderBlurTimeout) $timeout.cancel(folderBlurTimeout);
                if ($scope.isFolderDropdownOpen) {
                    closeFolderDropdownAndRevertText();
                } else {
                    if (clearFilter) {
                        $scope.folderFilter = '';
                    }

                    if (!$scope.isFolderDropdownOpen) {
                        $scope.isFolderDropdownOpen = true;
                        //Disable filtering initially (show all, even if datasetFilter has text)
                        $scope.filterActive = false;
                        addDocumentClickHandler();
                    }
                }


            };

            /**
             * Selects a folder, updates the config, and closes the dropdown. Called by ng-click on a dropdown item.
             * @param {{label: string, value: any}} folder - The folder object to select.
             */
            $scope.selectFolder = function (folder) {
                if (folderBlurTimeout) $timeout.cancel(folderBlurTimeout);

                $scope.selectedFolder = folder;
                $scope.config[$scope.configProperty] = folder ? folder.value : null;
                $scope.folderFilter = folder ? folder.label : '';

                // Close dropdown and clean up state
                $scope.isFolderDropdownOpen = false;
                $scope.isFolderFocused = false;
                removeDocumentClickHandler();
            };

            /**
             * Clears the current selection and input field. Called by the 'x' button.
             */
            $scope.clearFolderSelection = function () {
                $scope.selectFolder(null); // This clears all state
                $scope.openFolderDropdown(null, false); // Re-open the dropdown
                $element[0].querySelector('input').focus();
            };

            /**
             * Creates a new folder.
             */
            $scope.createNewFolder = function () {
                if ($scope.isCreatingFolder) return;

                $scope.isCreatingFolder = true;
                $scope.folderCreationError = '';

                const newFolderName = customFileUploaderService.generateAppFilename();

                if ($scope.folders.some(f => f.label.toLowerCase() === newFolderName.toLowerCase())) {
                    $timeout(() => {
                        $scope.folderCreationError = 'A folder with this name already exists.';
                        $scope.isCreatingFolder = false;
                    }, 0);
                    $timeout(() => {
                        $scope.folderCreationError = null;
                    }, 5000);
                    return;
                }

                customFileUploaderService.createFolder('LOCAL_STATIC', newFolderName, '')
                    .then(function (newFolder) {
                        $scope.$applyAsync(() => {
                            $scope.folders.push(newFolder);
                            $scope.selectFolder(newFolder);
                        });
                    })
                    .catch(function (error) {

                        $scope.folderCreationError = 'Failed to create folder: ' + (error.data?.detailedMessage || 'Unknown error');
                    })
                    .finally(function () {
                        $scope.isCreatingFolder = false;
                    });
            };

            // --- 4. Event Handlers (Bound to the Template) ---

            /**
             * Handles changes to the input field text. Called by ng-change.
             */
            $scope.handleFolderInputChange = function () {
                if ($scope.selectedFolder && $scope.folderFilter !== $scope.selectedFolder.label) {
                    $scope.selectedFolder = null;
                    $scope.config[$scope.configProperty] = null;
                }
                //Activate filtering now that the user is typing
                $scope.filterActive = true;
                // Automatically open dropdown if user starts typing
                if (!$scope.isFolderDropdownOpen && $scope.folderFilter) {
                    $scope.openFolderDropdown(null, false);
                }
            };

            /**
             * Handles the input blur event. Schedules the dropdown to close. Called by ng-blur.
             */
            $scope.handleFolderInputBlur = function () {
                folderBlurTimeout = $timeout(() => {
                    closeFolderDropdownAndRevertText();
                }, 200); // Delay allows click events on dropdown items to register first
            };

            //Handle input focus logic (open only if not already open)
            $scope.handleInputFocus = function () {
                if (!$scope.isFolderDropdownOpen) {
                    $scope.openFolderDropdown(false);
                }
            };

            /**
             * Filter function for ng-repeat to match folders against input text.
             */
            $scope.folderFilterFn = function (folder) {
                if (!$scope.filterActive) return true;
                if (!folder?.label) return false;
                if (!$scope.folderFilter) return true;
                return folder.label.toLowerCase().includes($scope.folderFilter.toLowerCase());
            };


            // --- 5. Initialization & Watchers ---

            const initialize = function () {

                if ($scope.getChoicesFromPython === 'false') {
                    // Case 1: Data passed from a service (sync)
                    const choices = DescParamsService.getSelectChoices($scope.configProperty) || [];
                    setAndProcessFolders(choices);
                } else if ($scope.isLanguageMappingsArray) {
                    // Case 2: Data from a parent scope binding (sync, but watched for changes)
                    setAndProcessFolders($scope.isLanguageMappingsArray);
                } else if ($scope.writePermission) {
                    // Case 3: Fetch from custom answers folder API (async)
                    customFileUploaderService.listSubFolders() //default to 'answers'
                        .then(response => setAndProcessFolders(response))
                        .catch(error => {

                            setAndProcessFolders([]);
                        });
                } else {
                    // Case 4: Default - Fetch from Python backend (async)

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

                    // Add projectKey if isFolder is true
                    if ($scope.isFolder === 'true' || $scope.isFolder === true) {
                        pythonCallParams.projectKey = $stateParams.projectKey;
                    }

                    ParamsHelperService.do(payload)
                        .then(data => setAndProcessFolders(data.choices || []))
                        .catch(error => {
                            setAndProcessFolders([]);
                        });

                }
            };

            // Watch for changes if data source is the bound languageMappings array
            if ($scope.isLanguageMappingsArray) {
                $scope.$watch('isLanguageMappingsArray', function (newVal, oldVal) {
                    if (!angular.equals(newVal, oldVal)) {

                        setAndProcessFolders(newVal);
                    }
                }, true);
            }

            // --- 6. Cleanup ---
            $scope.$on('$destroy', function () {
                removeDocumentClickHandler();
                if (folderBlurTimeout) $timeout.cancel(folderBlurTimeout);
            });

            // Run initialization
            initialize();

            // Listen for the event rebroadcasted by endUserConfiguration
            $scope.$on('folderRefreshRequested', function () {
                initialize(); // Re-fetch folders
            });

            // Handle array of trigger parameters
            if ($scope.triggerParameter && Array.isArray($scope.triggerParameter)) {
                $scope.triggerParameter.forEach(function (param) {
                    $scope.$watch(param, function (newValue, oldValue) {
                        if (newValue !== oldValue) {
                            initialize();
                        }
                    }, true);
                });
            } else if ($scope.triggerParameter) {
                // Single parameter (backwards compatibility)
                $scope.$watch('triggerParameter', function (newValue, oldValue) {
                    if (newValue !== oldValue) {
                        initialize();
                    }
                }, true);
            }

            $scope.$watch('config[configProperty]', function(newVal, oldVal) {
                if (newVal === null && oldVal !== null) {
                    // Config was cleared - reset internal state
                    $scope.selectedFolder = null;
                    $scope.folderFilter = '';
                }
            });



        }]
    };
}]);