/**
 * MapController Class
 *
 * Coordinates between the API adapter and map handler components.
 * Manages state, handles user interactions, and updates the UI accordingly.
 */
class MapController {
    /**
     * Constructor for MapController
     * @param {Object} options - Configuration options
     * @param {string} options.mapContainerId - ID of the map container element
     * @param {string} options.categorySelectId - ID of the category select element
     * @param {string} options.searchInputId - ID of the search input element
     * @param {boolean} options.debug - Enable debug logging
     */
    constructor(options = {}) {
        this.options = {
            mapContainerId: 'map',
            categorySelectId: 'category-select',
            searchInputId: 'search-input',
            includeChildrenId: 'include-children',
            locationListId: 'location-list',
            errorMessageId: 'error-message',
            noLocationsMessageId: 'no-locations-message',
            debug: false,
            ...options
        };

        // Create the API adapter
        this.apiAdapter = new MapApiAdapter({
            debug: this.options.debug
        });

        // Create the map handler
        this.mapHandler = new MapHandler({
            containerId: this.options.mapContainerId,
            onMarkerClick: this.handleMarkerClick.bind(this),
            debug: this.options.debug
        });

        // State
        this.state = {
            currentCategoryId: null,
            includeChildren: true,
            searchQuery: '',
            locations: [],
            categories: [],
            selectedLocation: null,
            previousCategoryId: null
        };

        // DOM elements
        this.elements = {
            categorySelect: document.getElementById(this.options.categorySelectId),
            searchInput: document.getElementById(this.options.searchInputId),
            includeChildren: document.getElementById(this.options.includeChildrenId),
            locationList: document.getElementById(this.options.locationListId),
            errorMessage: document.getElementById(this.options.errorMessageId),
            noLocationsMessage: document.getElementById(this.options.noLocationsMessageId)
        };

        this.log('MapController initialized');

        // Make the controller instance globally available
        window.mapController = this;
    }

    /**
     * Initialize the controller
     */
    async init() {
        try {
            this.log('Initializing MapController');

            // Set up event listeners
            this.setupEventListeners();

            // Load initial data
            await this.loadCategories();
            await this.loadLocations();

            this.log('MapController initialized successfully');
        } catch (error) {
            console.error('Error initializing MapController:', error);
        }
    }

    /**
     * Set up event listeners for UI elements
     */
    setupEventListeners() {
        // Category select change
        if (this.elements.categorySelect) {
            this.elements.categorySelect.addEventListener('change', this.handleCategoryChange.bind(this));
        }

        // Search input
        if (this.elements.searchInput) {
            this.elements.searchInput.addEventListener('keyup', this.debounce(this.handleSearch.bind(this), 300));
        }

        // Include children checkbox
        if (this.elements.includeChildren) {
            this.elements.includeChildren.addEventListener('change', this.handleIncludeChildrenChange.bind(this));
        }

        this.log('Event listeners set up');
    }

    /**
     * Load categories from the API and populate the select element
     */
    async loadCategories() {
        try {
            const categories = await this.apiAdapter.getCategories();
            this.state.categories = categories;

            // Populate category select if it exists
            if (this.elements.categorySelect) {
                // Clear existing options
                this.elements.categorySelect.innerHTML = '';

                // Add default option
                const defaultOption = document.createElement('option');
                defaultOption.value = '';
                defaultOption.textContent = 'All Categories';
                this.elements.categorySelect.appendChild(defaultOption);

                // Add category options
                categories.forEach(category => {
                    const option = document.createElement('option');
                    option.value = category.id;
                    option.textContent = category.name;
                    this.elements.categorySelect.appendChild(option);
                });
            }

            this.log('Categories loaded', categories);
        } catch (error) {
            console.error('Error loading categories:', error);
        }
    }

    /**
     * Load locations from the API and update the map
     */
    async loadLocations() {
        try {
            // Show loading state
            this.showLoading(true);

            // Store previous category ID
            this.state.previousCategoryId = this.state.currentCategoryId;

            // Determine if current category is a parent
            const isParentCategory = this.isParentCategory(this.state.currentCategoryId);

            // Force include_children to true for parent categories
            if (isParentCategory) {
                this.state.includeChildren = true;
                this.log('Forcing include_children=true for parent category', this.state.currentCategoryId);
            }

            // Get locations based on current state
            let result;

            if (this.state.searchQuery) {
                // Search by query
                result = await this.apiAdapter.searchLocations(this.state.searchQuery);
            } else if (this.state.currentCategoryId) {
                // Filter by category
                result = await this.apiAdapter.getCategoryLocations(
                    this.state.currentCategoryId,
                    this.state.includeChildren
                );
            } else {
                // Get all locations
                result = await this.apiAdapter.getLocations({count_by_category: 1});
            }

            const { locations, meta } = result;
            this.state.locations = locations;

            // Log detailed information about the results
            this.log('API Response Meta:', meta);
            this.log('Locations loaded', {
                count: locations.length,
                categoryId: this.state.currentCategoryId,
                includeChildren: this.state.includeChildren,
                previousCategoryId: this.state.previousCategoryId,
                isParentCategory: isParentCategory
            });

            // Clear any previous error or no-results messages
            this.clearMessages();

            // Update the map with new locations
            this.mapHandler.clearLocations();

            if (locations.length > 0) {
                this.mapHandler.addLocations(locations);
                this.mapHandler.fitBounds();
            } else {
                this.log('No locations found for category', this.state.currentCategoryId);

                // Display a message on the map if there are no locations
                if (this.elements.noLocationsMessage) {
                    this.elements.noLocationsMessage.style.display = 'block';
                }

                // If no locations found for parent category, try reloading with direct locations query
                if (isParentCategory && this.state.includeChildren) {
                    this.log('No locations found with include_children, trying direct query to child categories');

                    // Get direct query to child categories
                    try {
                        const directResult = await this.apiAdapter.getChildCategoryLocations(
                            this.state.currentCategoryId,
                            this.request && this.request.has('is_active') ? this.request.is_active : true
                        );

                        if (directResult.locations && directResult.locations.length > 0) {
                            this.log('Found locations with direct query', directResult.locations.length);
                            this.mapHandler.addLocations(directResult.locations);
                            this.mapHandler.fitBounds();
                            this.updateLocationList(directResult.locations);
                            this.state.locations = directResult.locations;
                        }

                        // Update counts regardless
                        if (window.categoryFilter && directResult.meta && directResult.meta.counts) {
                            window.categoryFilter.updateCategoryCounts(directResult.meta.counts);
                        }
                    } catch (directError) {
                        console.error('Error with direct query:', directError);
                    }
                }
            }

            // Update location list if it exists
            this.updateLocationList(locations);

            // Update CategoryFilter component if it exists and category counts are provided
            if (window.categoryFilter && meta && meta.counts) {
                this.log('Updating category counts from API response', meta.counts);
                window.categoryFilter.updateCategoryCounts(meta.counts);
            }

            // Hide loading state
            this.showLoading(false);
        } catch (error) {
            console.error('Error loading locations:', error);
            this.showLoading(false);

            // Show error message
            if (this.elements.errorMessage) {
                this.elements.errorMessage.textContent = 'Error loading locations: ' + error.message;
                this.elements.errorMessage.style.display = 'block';
            }
        }
    }

    /**
     * Handle category select change
     * @param {Event} event - Change event
     */
    async handleCategoryChange(event) {
        const categoryId = event.target.value;
        this.log(`Category change event:`, {
            newCategoryId: categoryId,
            prevCategoryId: this.state.currentCategoryId
        });

        // If category changed, reset search
        if (categoryId !== this.state.currentCategoryId) {
            this.state.searchQuery = '';
            if (this.elements.searchInput) {
                this.elements.searchInput.value = '';
            }

            // Clear any previous error or no-results messages
            this.clearMessages();
        }

        this.state.currentCategoryId = categoryId || null;

        // Reset include_children to true when selecting any category
        // This ensures that parent categories will show all child locations by default
        this.state.includeChildren = true;
        if (this.elements.includeChildren) {
            this.elements.includeChildren.checked = true;
        }

        // Determine if this is a parent category
        const isParentCategory = this.isParentCategory(categoryId);
        this.log(`Category changed to ${categoryId} (parent: ${isParentCategory})`);

        // If parent category is selected, ensure include_children is true
        // If child category is selected, no special handling needed
        if (isParentCategory) {
            this.state.includeChildren = true;
            if (this.elements.includeChildren) {
                this.elements.includeChildren.checked = true;
                // Optionally disable the checkbox for parent categories
                // this.elements.includeChildren.disabled = true;
            }
        } else {
            // Re-enable the checkbox for child categories
            if (this.elements.includeChildren) {
                this.elements.includeChildren.disabled = false;
            }
        }

        await this.loadLocations();
    }

    /**
     * Check if a category is a parent category
     * @param {string} categoryId - Category ID to check
     * @returns {boolean} True if the category is a parent
     */
    isParentCategory(categoryId) {
        if (!categoryId) return false;

        // If categoryFilter is available, use its parent-child map
        if (window.categoryFilter && window.categoryFilter.isParentCategory) {
            return window.categoryFilter.isParentCategory(categoryId);
        }

        // Fallback: check if the category has children in our categories list
        return this.state.categories.some(cat =>
            cat.parent_id && cat.parent_id.toString() === categoryId.toString());
    }

    /**
     * Handle search input
     * @param {Event} event - Keyup event
     */
    async handleSearch(event) {
        const query = event.target.value.trim();

        // Only reload if query changed
        if (query !== this.state.searchQuery) {
            this.state.searchQuery = query;
            this.log(`Search query changed to "${query}"`);

            await this.loadLocations();
        }
    }

    /**
     * Handle include children checkbox change
     * @param {Event} event - Change event
     */
    async handleIncludeChildrenChange(event) {
        const includeChildren = event.target.checked;

        // Only reload if value changed
        if (includeChildren !== this.state.includeChildren) {
            this.state.includeChildren = includeChildren;
            this.log(`Include children changed to ${includeChildren}`);

            // Only reload if we have a category selected
            if (this.state.currentCategoryId) {
                await this.loadLocations();
            }
        }
    }

    /**
     * Handle marker click on the map
     * @param {Object} location - Location data for the clicked marker
     */
    handleMarkerClick(location) {
        this.state.selectedLocation = location;
        this.log('Location selected', location);

        // Update UI to highlight the selected location
        this.highlightSelectedLocation(location);
    }

    /**
     * Update the location list with the current locations
     * @param {Array} locations - List of locations to display
     */
    updateLocationList(locations) {
        if (!this.elements.locationList) return;

        // Clear existing list
        this.elements.locationList.innerHTML = '';

        if (locations.length === 0) {
            // Show no results message
            const noResults = document.createElement('div');
            noResults.className = 'no-results';
            noResults.textContent = 'No locations found';
            this.elements.locationList.appendChild(noResults);
            return;
        }

        // Create list items for each location
        locations.forEach(location => {
            const item = document.createElement('div');
            item.className = 'location-item';
            item.dataset.id = location.id;

            const name = document.createElement('h3');
            name.textContent = location.name;
            item.appendChild(name);

            if (location.address) {
                const address = document.createElement('p');
                address.textContent = location.address;
                item.appendChild(address);
            }

            if (location.category) {
                const category = document.createElement('span');
                category.className = 'category-badge';
                category.textContent = typeof location.category === 'object' ?
                    location.category.name : location.category;
                item.appendChild(category);
            }

            // Add click handler
            item.addEventListener('click', () => {
                const loc = locations.find(l => l.id == location.id);
                if (loc) {
                    this.handleListItemClick(loc);
                }
            });

            this.elements.locationList.appendChild(item);
        });
    }

    /**
     * Handle click on a location list item
     * @param {Object} location - Location data for the clicked item
     */
    handleListItemClick(location) {
        this.state.selectedLocation = location;

        // Pan to the location on the map
        this.mapHandler.panToLocation(location);

        // Open the info window for the location
        this.mapHandler.showInfoWindow(location);

        // Highlight the item in the list
        this.highlightSelectedLocation(location);
    }

    /**
     * Highlight the selected location in the list and on the map
     * @param {Object} location - Location to highlight
     */
    highlightSelectedLocation(location) {
        // Highlight on map
        this.mapHandler.highlightMarker(location.id);

        // Highlight in list
        if (this.elements.locationList) {
            // Remove highlight from all items
            const items = this.elements.locationList.querySelectorAll('.location-item');
            items.forEach(item => item.classList.remove('selected'));

            // Add highlight to selected item
            const selectedItem = this.elements.locationList.querySelector(`.location-item[data-id="${location.id}"]`);
            if (selectedItem) {
                selectedItem.classList.add('selected');
                selectedItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
            }
        }
    }

    /**
     * Show or hide loading state
     * @param {boolean} isLoading - Whether the app is loading
     */
    showLoading(isLoading) {
        // Add loading indicator to the map
        if (isLoading) {
            this.mapHandler.showLoadingIndicator();
        } else {
            this.mapHandler.hideLoadingIndicator();
        }

        // Add loading class to container elements
        const containers = [
            this.elements.categorySelect,
            this.elements.locationList
        ].filter(el => el !== null);

        containers.forEach(el => {
            if (isLoading) {
                el.classList.add('loading');
            } else {
                el.classList.remove('loading');
            }
        });
    }

    /**
     * Debounce function for event handlers
     * @param {Function} func - Function to debounce
     * @param {number} wait - Wait time in milliseconds
     * @returns {Function} Debounced function
     */
    debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    /**
     * Log debug messages if debug is enabled
     * @param {string} message - Debug message
     * @param {*} data - Optional data to log
     */
    log(message, data) {
        if (this.options.debug) {
            if (data !== undefined) {
                console.log(`[MapController] ${message}`, data);
            } else {
                console.log(`[MapController] ${message}`);
            }
        }
    }

    /**
     * Clear error and no-locations messages
     */
    clearMessages() {
        if (this.elements.errorMessage) {
            this.elements.errorMessage.style.display = 'none';
        }

        if (this.elements.noLocationsMessage) {
            this.elements.noLocationsMessage.style.display = 'none';
        }
    }

    /**
     * Function used by CategoryFilter to update category filters
     * @param {string} categoryId - Category ID
     * @param {Object} options - Options for the category change
     * @param {boolean} options.include_children - Whether to include child categories
     * @param {boolean} options.resetPrevious - Whether to reset previous selections
     * @param {string} options.parentId - Parent category ID (for child categories)
     * @param {boolean} options.isParent - Whether this is a parent category
     */
    handleCategoryFilterChange(categoryId, options) {
        this.log('Category filter changed:', {
            categoryId,
            options
        });

        // Update state
        this.state.currentCategoryId = categoryId;
        if (options.include_children !== undefined) {
            this.state.includeChildren = options.include_children;
        }

        // Clear previous category filter if resetting
        if (options.resetPrevious) {
            this.state.categoryFilters = [];
        }

        // For parent categories with child locations, we want to use the special API endpoint
        if (options.isParent === true) {
            this.log('Using special parent category handling for', categoryId);
            this.loadParentCategoryLocations(categoryId);
        } else if (options.isParent === false) {
            // For child categories, use direct category filter
            this.log('Using child category handling for', categoryId);
            this.loadChildCategoryLocations(categoryId);
        } else {
            // For other cases or no special handling
            this.loadLocations();
        }
    }

    /**
     * Special method for loading parent category locations that also checks child categories
     * @param {string} parentCategoryId - Parent category ID
     */
    async loadParentCategoryLocations(parentCategoryId) {
        try {
            // Show loading state
            this.showLoading(true);

            this.log('Loading locations for parent category with ID:', parentCategoryId);

            // First try the direct parent_category_id approach which is optimized for this case
            let result = await this.apiAdapter.getChildCategoryLocations(
                parentCategoryId,
                true // Always use active locations
            );

            let { locations, meta } = result;
            this.log('Parent category API Response (direct method):', {
                count: locations.length,
                meta: meta
            });

            // If no results, try the normal approach with category_id and include_children
            if (!locations || locations.length === 0) {
                this.log('No locations found with direct child query, trying normal include_children approach');

                // Try with category_id and include_children=true
                result = await this.apiAdapter.getCategoryLocations(
                    parentCategoryId,
                    true // Always include children for parent categories
                );

                locations = result.locations;
                meta = result.meta;

                this.log('Parent category API Response (fallback method):', {
                    count: locations ? locations.length : 0,
                    meta: meta
                });
            }

            // Log detailed information about the API response
            if (locations && locations.length > 0) {
                this.log('Successfully found locations for parent category', {
                    count: locations.length,
                    firstLocation: locations[0]
                });
            } else {
                this.log('No locations found for parent category after all attempts', {
                    parent_id: parentCategoryId
                });
            }

            // Update state with found locations
            this.state.locations = locations || [];

            // Update the map with new locations
            this.mapHandler.clearLocations();

            if (locations && locations.length > 0) {
                this.mapHandler.addLocations(locations);
                this.mapHandler.fitBounds();
                this.log('Added', locations.length, 'locations to map');
            } else {
                this.log('No locations found for parent category', parentCategoryId);

                // Display a message on the map if there are no locations
                if (this.elements.noLocationsMessage) {
                    this.elements.noLocationsMessage.style.display = 'block';
                }
            }

            // Update location list if it exists
            this.updateLocationList(locations || []);

            // Update CategoryFilter component if it exists and category counts are provided
            if (window.categoryFilter && meta && meta.counts) {
                this.log('Updating category counts from API response', meta.counts);
                window.categoryFilter.updateCategoryCounts(meta.counts);
            }

            // Hide loading state
            this.showLoading(false);
        } catch (error) {
            console.error('Error loading parent category locations:', error);
            this.showLoading(false);

            // Show error message
            if (this.elements.errorMessage) {
                this.elements.errorMessage.textContent = 'Error loading locations: ' + error.message;
                this.elements.errorMessage.style.display = 'block';
            }
        }
    }

    /**
     * Special method for loading child category locations
     * @param {string} childCategoryId - Child category ID
     */
    async loadChildCategoryLocations(childCategoryId) {
        try {
            // Show loading state
            this.showLoading(true);

            this.log('Loading locations for child category with ID:', childCategoryId);

            // Update state first
            this.state.currentCategoryId = childCategoryId;
            this.state.includeChildren = false;

            // Use the normal API with specific category filter
            const result = await this.apiAdapter.getLocations({
                category: childCategoryId,
                include_children: false // Don't include children for child categories
            });

            const { locations, meta } = result;
            this.log('Child category API Response:', {
                count: locations ? locations.length : 0,
                meta: meta,
                categoryId: childCategoryId
            });

            // Update state with found locations
            this.state.locations = locations || [];

            // Clear any previous error or no-results messages
            this.clearMessages();

            // Update the map with new locations
            this.mapHandler.clearLocations();

            if (locations && locations.length > 0) {
                this.mapHandler.addLocations(locations);
                this.mapHandler.fitBounds();
                this.log('Added', locations.length, 'locations to map for child category');
            } else {
                this.log('No locations found for child category', childCategoryId);

                // Display a message on the map if there are no locations
                if (this.elements.noLocationsMessage) {
                    this.elements.noLocationsMessage.style.display = 'block';
                }
            }

            // Update location list if it exists
            this.updateLocationList(locations || []);

            // Update CategoryFilter component if it exists and category counts are provided
            if (window.categoryFilter && meta && meta.counts) {
                this.log('Updating category counts from API response', meta.counts);
                window.categoryFilter.updateCategoryCounts(meta.counts);
            }

            // Update ModernCategoryFilter component if it exists and category counts are provided
            if (window.modernCategoryFilter && meta && meta.counts) {
                this.log('Updating modern category filter counts from API response', meta.counts);
                window.modernCategoryFilter.updateCategoryCounts(meta.counts);
            }

            // Hide loading state
            this.showLoading(false);
        } catch (error) {
            console.error('Error loading child category locations:', error);
            this.showLoading(false);

            // Show error message
            if (this.elements.errorMessage) {
                this.elements.errorMessage.textContent = 'Error loading locations: ' + error.message;
                this.elements.errorMessage.style.display = 'block';
            }
        }
    }
}