import {debounce} from '../../../themes/springernature/js/helpers';

class EdsCAutocomplete {
	constructor(wrapper, options = {}) {
		if (!wrapper) {
			console.error('Could not init autocomplete component. Wrapper not found.');
			return;
		}

		this.wrapper = wrapper;
		this.input = wrapper.querySelector('input[type="text"]');
		this.dropdown = wrapper.querySelector('.eds-c-autocomplete__dropdown');
		this.noResultMsg = options.noResultMsg || 'No results found for';
		this.loadingMsg = options.loadingMsg || 'Loading...';
		this.errorMsg = options.errorMsg || 'Unable to load results. Please try again.';
        
		if (!this.input || !this.dropdown) {
			console.error('Could not init autocomplete component. Missing input or dropdown element.');
			return;
		}

		// Component state
		this.isOpen = false;
		this.currentFocus = -1;
		this.filteredResults = [];
		this.isLoading = false;
        
		// Configuration
		this.minChars = options.minChars || 2;
		this.debounceDelay = options.debounceDelay || 300;
		this.apiUrl = options.apiUrl || null;
		this.maxResults = options.maxResults || 20;
        
		// API configuration
		this.apiQueryParam = options.apiQueryParam || 'q';
		this.apiMethod = options.apiMethod || 'GET';
		this.apiHeaders = options.apiHeaders || {};
		
		// Read maxQueryLength: prioritize HTML maxlength attribute, then JS config, then default to 100
		const htmlMaxLength = this.input.getAttribute('maxlength');
		this.maxQueryLength = htmlMaxLength ? parseInt(htmlMaxLength, 10) : (options.maxQueryLength || 100);
		
		this.transformResponse = options.transformResponse || null;
		this.apiCredentials = (typeof options.apiCredentials !== 'undefined') ? options.apiCredentials : 'same-origin';
		this.apiBodyFormat = options.apiBodyFormat || 'json'; // 'json' or 'urlencoded'

		// Bind methods
		this._handleKeydown = this._handleKeydown.bind(this);
		this._handleResultClick = this._handleResultClick.bind(this);
		this._handleDocumentClick = this._handleDocumentClick.bind(this);
		this._handleFocus = this._handleFocus.bind(this);
	
		// Create debounced input handler with correct options object
		const originalHandleInput = this._handleInput.bind(this);
		this._handleInput = debounce(originalHandleInput, {wait: this.debounceDelay});

		this._init();
	}	
	
	_init() {
		// Set ARIA attributes on input
		this.input.setAttribute('role', 'combobox');
		this.input.setAttribute('aria-autocomplete', 'list');
		this.input.setAttribute('aria-expanded', 'false');
		this.input.setAttribute('aria-controls', this.dropdown.id || this._generateDropdownId());
        
		// Ensure dropdown has proper attributes
		if (!this.dropdown.id) {
			this.dropdown.id = this._generateDropdownId();
		}
		this.dropdown.setAttribute('role', 'listbox');
		this.dropdown.setAttribute('aria-label', 'Search results');

		// Create persistent aria-live region for announcements
		this._createAriaLiveRegion();

		this.input.addEventListener('input', this._handleInput);
		this.input.addEventListener('keydown', this._handleKeydown);
		this.input.addEventListener('focus', this._handleFocus);
		document.addEventListener('click', this._handleDocumentClick);
	}

	_escapeRegex(string) {
		return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
	}

	_escapeHtml(str) {
		if (!str) return '';
		const div = document.createElement('div');
		div.textContent = str;
		return div.innerHTML;
	}

	_generateDropdownId() {
		const uniqueId = `eds-c-autocomplete-dropdown-${Date.now()}`;
		return uniqueId;
	}

	_createAriaLiveRegion() {
		// Create a persistent aria-live region outside the dropdown
		this.ariaLiveRegion = document.createElement('div');
		this.ariaLiveRegion.setAttribute('aria-live', 'assertive');
		this.ariaLiveRegion.setAttribute('aria-atomic', 'true');
		this.ariaLiveRegion.setAttribute('class', 'u-visually-hidden');
		this.wrapper.appendChild(this.ariaLiveRegion);
	}

	_announce(message) {
		if (!this.ariaLiveRegion) return;
		
		// Clear then add message to ensure it's announced
		this.ariaLiveRegion.textContent = '';
		
		// Use requestAnimationFrame to ensure change is detected
		requestAnimationFrame(() => {
			this.ariaLiveRegion.textContent = message;
		});
	}

	async _handleInput(event) {
		const query = event.target.value.trim();

		if (query.length < this.minChars) {
			this._closeDropdown();
			return;
		}
        
		if (!this.apiUrl) {
			console.warn('No apiUrl provided for autocomplete; component is configured for API-only usage.');
			this.filteredResults = [];
			this._renderNoResults(query);
			return;
		}

		await this._fetchResults(query);
        
		if (this.filteredResults.length > 0) {
			this._renderResults();
			this._openDropdown();
		} else {
			this._renderNoResults(query);
			this._openDropdown();
		}
	}

	_handleFocus() {
		const query = this.input.value.trim();
        
		if (query.length >= this.minChars && this.filteredResults.length > 0) {
			this._openDropdown();
		}
	}

	_handleKeydown(event) {
		if (!this.isOpen) {
			return;
		}

		const results = this.dropdown.querySelectorAll('[role="option"]');
        
		switch (event.key) {
		case 'ArrowDown':
			event.preventDefault();
			this._moveFocus(1, results);
			break;
		case 'ArrowUp':
			event.preventDefault();
			this._moveFocus(-1, results);
			break;
		case 'Enter':
			event.preventDefault();
			if (this.currentFocus > -1 && results[this.currentFocus]) {
				this._selectResult(results[this.currentFocus]);
			}
			break;
		case 'Escape':
			event.preventDefault();
			this._closeDropdown();
			break;
		case 'Tab':
			this._closeDropdown();
			break;
		default:
			break;
		}
	}

	_moveFocus(direction, results) {
		if (results.length === 0) {
			return;
		}

		// Remove previous active state
		if (this.currentFocus > -1 && results[this.currentFocus]) {
			results[this.currentFocus].classList.remove('eds-c-autocomplete__item--active');
			results[this.currentFocus].setAttribute('aria-selected', 'false');
		}

		// Calculate new focus
		this.currentFocus += direction;

		// Wrap around
		if (this.currentFocus >= results.length) {
			this.currentFocus = 0;
		} else if (this.currentFocus < 0) {
			this.currentFocus = results.length - 1;
		}

		// Apply new active state
		results[this.currentFocus].classList.add('eds-c-autocomplete__item--active');
		results[this.currentFocus].setAttribute('aria-selected', 'true');
        
		// Update aria-activedescendant
		this.input.setAttribute('aria-activedescendant', results[this.currentFocus].id);

		// Scroll into view if needed
		results[this.currentFocus].scrollIntoView({
			block: 'nearest',
			behavior: 'smooth'
		});
	}

	_handleResultClick(event) {
		const resultElement = event.target.closest('[role="option"]');
		if (resultElement) {
			this._selectResult(resultElement);
		}
	}

	_selectResult(resultElement) {
		const value = resultElement.dataset.value;
		const displayText = resultElement.dataset.displayText;
        
		this.input.value = displayText || value;
        
		// Store the selected data if needed
		this.selectedItem = this.filteredResults.find(item => 
			item.name === (displayText || value)
		);

		// Dispatch custom event for consumers
		const selectEvent = new CustomEvent('autocomplete:select', {
			detail: {
				value: value,
				displayText: displayText,
				item: this.selectedItem
			},
			bubbles: true
		});
		this.wrapper.dispatchEvent(selectEvent);

		// Clear filtered results to prevent reopening on focus
		this.filteredResults = [];
		this._closeDropdown();
		this.input.focus();
	}

	_handleDocumentClick(event) {
		if (!this.wrapper.contains(event.target)) {
			this._closeDropdown();
		}
	}

	async _fetchResults(query) {
		// Validate query length
		if (query.length > this.maxQueryLength) {
			console.warn('Query exceeds maximum length');
			return;
		}

		this.isLoading = true;
		this._renderLoading();
		this._openDropdown();

		try {
			let url = this.apiUrl;
			let fetchOptions = {
				method: this.apiMethod,
				headers: {
					...this.apiHeaders // Allow custom headers (auth, CSRF token, etc.)
				},
				credentials: this.apiCredentials
			};

			// Build URL with query parameter for GET requests
			if (this.apiMethod === 'GET') {
				const separator = url.includes('?') ? '&' : '?';
				url = `${url}${separator}${this.apiQueryParam}=${encodeURIComponent(query)}`;
			} else {
				// For POST requests, add query to body based on format
				if (this.apiBodyFormat === 'urlencoded') {
					fetchOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded';
					const params = new URLSearchParams();
					params.append(this.apiQueryParam, query);
					fetchOptions.body = params.toString();
				} else {
					// Default to JSON
					fetchOptions.headers['Content-Type'] = 'application/json';
					fetchOptions.body = JSON.stringify({
						[this.apiQueryParam]: query
					});
				}
			}

			const response = await fetch(url, fetchOptions);

			if (!response.ok) {
				throw new Error(`API error: ${response.status}`);
			}

			let data = await response.json();

			// Transform response if custom transformer provided
			if (this.transformResponse && typeof this.transformResponse === 'function') {
				data = this.transformResponse(data);
			}

			// Validate response is an array
			if (!Array.isArray(data)) {
				console.warn('API response is not an array, expected array of results');
				data = [];
			}

			this.filteredResults = data.slice(0, this.maxResults);
            
		} catch (error) {
			console.error('Error fetching autocomplete results:', error);
			this._renderError();
			this.filteredResults = [];
		} finally {
			this.isLoading = false;
		}
	}

	_renderResults() {
		// Restore listbox role for results
		this.dropdown.setAttribute('role', 'listbox');
		this.dropdown.setAttribute('aria-label', 'Search results');
        
		const fragment = document.createDocumentFragment();

		this.filteredResults.forEach((item, index) => {
			const resultElement = this._createResultElement(item, index);
			fragment.appendChild(resultElement);
		});

		this.dropdown.innerHTML = '';
		this.dropdown.appendChild(fragment);

		// Add click listeners to results
		this.dropdown.addEventListener('click', this._handleResultClick);
	}

	_createResultElement(item, index) {
		const element = document.createElement('div');
		element.className = 'eds-c-autocomplete__item';
		element.setAttribute('role', 'option');
		element.setAttribute('id', `${this.dropdown.id}-option-${index}`);
		element.setAttribute('aria-selected', 'false');
		element.dataset.value = item.id;
		element.dataset.displayText = item.name;

		// Highlight matching text
		const query = this.input.value.trim().toLowerCase();
		const nameHtml = this._highlightMatch(item.name, query);
        
		const metaHtml = item.city ? 
			`<div class="eds-c-autocomplete__item-meta">${this._escapeHtml(item.city)}, ${this._escapeHtml(item.country)}</div>` : '';
    
		element.innerHTML = `
            <div class="eds-c-autocomplete__item-name">${nameHtml}</div>
            ${metaHtml}
        `;

		return element;
	}

	_highlightMatch(text, query) {
		if (!text || !query) return text || '';
        
		// Escape HTML entities in both text and query first
		const escapedText = this._escapeHtml(text);
		const escapedQuery = this._escapeHtml(query);
        
		// Then escape regex special characters from the HTML-escaped query
		const regexSafeQuery = this._escapeRegex(escapedQuery);
        
		return escapedText.replace(
			new RegExp(`(${regexSafeQuery})`, 'gi'),
			'<strong>$1</strong>'
		);
	}

	_renderNoResults(query) {
		// Remove listbox role
		this.dropdown.removeAttribute('role');
		this.dropdown.removeAttribute('aria-label');
        
		// Clear dropdown and show visual message
		this.dropdown.innerHTML = `
			<div class="eds-c-autocomplete__no-results">
				<p>${this.noResultMsg} "${this._escapeHtml(query)}"</p>
			</div>
		`;
        
		// Announce to screen readers via persistent live region
		this._announce(`${this.noResultMsg} "${query}"`);
	}

	_renderLoading() {
		this.dropdown.removeAttribute('role');
		this.dropdown.removeAttribute('aria-label');
        
		// Clear dropdown and show visual loading message
		this.dropdown.innerHTML = `
			<div class="eds-c-autocomplete__loading">
				${this.loadingMsg}
			</div>
		`;
        
		// Announce to screen readers via persistent live region
		this._announce(this.loadingMsg);
	}

	_renderError() {
		this.dropdown.removeAttribute('role');
		this.dropdown.removeAttribute('aria-label');
        
		// Clear dropdown and show visual error message
		this.dropdown.innerHTML = `
			<div class="eds-c-autocomplete__error">
				${this.errorMsg}
			</div>
		`;
        
		// Announce to screen readers via persistent live region
		this._announce(this.errorMsg);
	}

	_openDropdown() {
		if (this.isOpen) {
			return;
		}

		this.isOpen = true;
		this.dropdown.classList.add('eds-c-autocomplete__dropdown--open');
		this.input.setAttribute('aria-expanded', 'true');
	}

	_closeDropdown() {
		if (!this.isOpen) {
			return;
		}

		this.isOpen = false;
		this.currentFocus = -1;
		this.dropdown.classList.remove('eds-c-autocomplete__dropdown--open');
		this.input.setAttribute('aria-expanded', 'false');
		this.input.removeAttribute('aria-activedescendant');
	}

	/**
     * Cleans up event listeners and ARIA attributes.
     * Call this method before:
     * - Removing the component from the DOM
     * - Re-initializing on the same element
     * - Navigating in SPAs
     * - Running cleanup in unit tests
     * 
     * For traditional multi-page apps with full page reloads, 
     * you typically don't need to call this.
    */

	destroy() {
		if (!this.input || !this.dropdown) {
			return;
		}
        
		this.input.removeEventListener('input', this._handleInput);
		this.input.removeEventListener('keydown', this._handleKeydown);
		this.input.removeEventListener('focus', this._handleFocus);
		this.dropdown.removeEventListener('click', this._handleResultClick);
		document.removeEventListener('click', this._handleDocumentClick);
        
		// Remove aria-live region
		if (this.ariaLiveRegion && this.ariaLiveRegion.parentNode) {
			this.ariaLiveRegion.parentNode.removeChild(this.ariaLiveRegion);
		}
        
		// Reset ARIA attributes
		this.input.removeAttribute('role');
		this.input.removeAttribute('aria-autocomplete');
		this.input.removeAttribute('aria-expanded');
		this.input.removeAttribute('aria-controls');
		this.input.removeAttribute('aria-activedescendant');
	}
}

export default EdsCAutocomplete;