/**
 * Chat API Client
 *
 * Handles communication with the WordPress AJAX API for chat functionality,
 * managing conversation state, message storage, and API interactions.
 *
 * @file
 * @since 1.0.0
 * @package AgenticWP
 */

/**
 * Chat API client for managing conversations and API communication.
 *
 * @class
 * @since 1.0.0
 */
class ChatAPI {
	/**
	 * Initializes the Chat API client.
	 *
	 * @param {StorageWrapper} storage - Storage wrapper instance for persistence.
	 * @param {ChatUIHelpers} uiHelpers - UI helpers instance for rendering.
	 */
	constructor(storage, uiHelpers) {
		this.storage = storage;
		this.uiHelpers = uiHelpers;
		this.abortController = null;
		this.state = {
			conversationId: this.getStoredConversationId(),
			messages: this.getStoredMessages()
		};
	}

	/**
	 * Aborts the current API request if one is in progress.
	 *
	 * @return {boolean} True if a request was aborted.
	 */
	abortRequest() {
		if (this.abortController) {
			this.abortController.abort();
			this.abortController = null;
			return true;
		}
		return false;
	}

	/**
	 * Sends a message to the API and handles the response.
	 *
	 * @param {string} message - User message to send.
	 * @param {HTMLTextAreaElement} messageInput - Input element to clear after sending.
	 * @param {HTMLElement} messagesContainer - Container to display messages in.
	 * @param {Function} setLoadingCallback - Callback to control loading state.
	 * @return {Promise<void>}
	 */
	async sendMessage(message, messageInput, messagesContainer, setLoadingCallback) {
		if (!await this.checkApiKeyStatus()) {
			this.showError('Please configure your OpenAI API key in Settings → AgenticWP.', messagesContainer);
			return;
		}

		this.addMessage(message, 'user', messagesContainer);
		messageInput.value = '';
		this.uiHelpers.autoResizeTextarea(messageInput);
		setLoadingCallback(true);

		const thinkingIndicator = this.addLoadingMessage('Thinking...', messagesContainer);

		try {
			const response = await this.makeApiRequest(message);
            
			if (response.success) {
				await this.handleSuccessfulResponse(response, thinkingIndicator, messagesContainer);
			} else {
				throw new Error(response.data?.message || 'Failed to get response');
			}
            
		} catch (error) {
			this.handleSendError(error, thinkingIndicator, messagesContainer);
		} finally {
			setLoadingCallback(false);
		}
	}

	/**
	 * Processes a successful API response and displays reasoning and message.
	 *
	 * @param {Object} response - API response object.
	 * @param {HTMLElement} thinkingIndicator - Loading indicator to remove.
	 * @param {HTMLElement} messagesContainer - Container to display messages in.
	 * @return {Promise<void>}
	 */
	async handleSuccessfulResponse(response, thinkingIndicator, messagesContainer) {
		thinkingIndicator.remove();
        
		if (response.data.reasoning && response.data.reasoning.trim().length > 0) {
			await this.displayReasoningWithDelay(response.data.reasoning, messagesContainer);
		}
        
		this.addMessage(response.data.message, 'assistant', messagesContainer, response.data.reasoning, true);
		this.updateConversationState(response.data);
	}

	/**
	 * Displays reasoning with a delay and loading indicators.
	 *
	 * @param {string} reasoning - Reasoning text to display.
	 * @param {HTMLElement} messagesContainer - Container to display in.
	 * @return {Promise<void>}
	 */
	async displayReasoningWithDelay(reasoning, messagesContainer) {
		this.addReasoningElement(reasoning, messagesContainer);

		await this.sleep(500);
		const responseIndicator = this.addLoadingMessage('Generating response...', messagesContainer);
		await this.sleep(1000);
		responseIndicator.remove();
	}

	/**
	 * Updates conversation state with response data.
	 *
	 * @param {Object} responseData - API response data.
	 */
	updateConversationState(responseData) {
		if (responseData.conversation_id) {
			this.state.conversationId = responseData.conversation_id;
			this.storeConversationId(this.state.conversationId);
		}
		this.storeMessages();
	}

	/**
	 * Handles send errors by displaying error message.
	 *
	 * @param {Error} error - Error object.
	 * @param {HTMLElement} thinkingIndicator - Loading indicator to remove.
	 * @param {HTMLElement} messagesContainer - Container to display error in.
	 */
	handleSendError(error, thinkingIndicator, messagesContainer) {
		thinkingIndicator.remove();

		if (error.name === 'AbortError') {
			return;
		}

		this.showError(error.message || 'Something went wrong. Please try again.', messagesContainer);
	}

	/**
	 * Makes API request to send message.
	 *
	 * @param {string} message - User message to send.
	 * @return {Promise<Object>} API response.
	 */
	async makeApiRequest(message) {
		this.abortController = new AbortController();

		const formData = new FormData();
		formData.append('action', 'agenticwp_send_message');
		formData.append('message', message);
		formData.append('_ajax_nonce', agenticwp_ajax.nonce);

		if (this.state.conversationId) {
			formData.append('conversation_id', this.state.conversationId);
		}

		const response = await fetch(agenticwp_ajax.ajax_url, {
			method: 'POST',
			body: formData,
			credentials: 'same-origin',
			signal: this.abortController.signal
		});

		this.abortController = null;

		if (!response.ok) {
			const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
			throw error;
		}

		const result = await response.json();

		return result;
	}

	/**
	 * Checks if API key is configured.
	 *
	 * @return {Promise<boolean>} True if API key exists.
	 */
	async checkApiKeyStatus() {
		try {
			const formData = new FormData();
			formData.append('action', 'agenticwp_check_api_key');
			formData.append('_ajax_nonce', agenticwp_ajax.nonce);

			const response = await fetch(agenticwp_ajax.ajax_url, {
				method: 'POST',
				body: formData,
				credentials: 'same-origin'
			});

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

			const result = await response.json();
			return result.success && result.data.has_api_key;

		} catch (error) {
			return true;
		}
	}

	/**
	 * Adds a message to the chat interface and stores it.
	 *
	 * @param {string} content - Message content.
	 * @param {string} type - Message type ('user' or 'assistant').
	 * @param {HTMLElement} messagesContainer - Container to add message to.
	 * @param {string} reasoning - Optional reasoning text.
	 * @return {HTMLElement} Created message element.
	 */
	addMessage(content, type, messagesContainer, reasoning = '') {
		const messageElement = this.uiHelpers.createMessageElement(content, type);

		messagesContainer.appendChild(messageElement);
		this.scrollToBottom(messagesContainer);
        
		this.state.messages.push({
			type,
			content,
			reasoning: reasoning || '',
			timestamp: new Date().toISOString()
		});
        
		return messageElement;
	}

	/**
	 * Adds a loading indicator message.
	 *
	 * @param {string} customText - Custom loading text.
	 * @param {HTMLElement} messagesContainer - Container to add to.
	 * @return {HTMLElement} Loading element.
	 */
	addLoadingMessage(customText = 'Assistant is typing', messagesContainer) {
		const loadingElement = this.uiHelpers.createElement('div', 'agenticwp-message loading');
		loadingElement.innerHTML = this.uiHelpers.createLoadingIndicator(customText);
        
		messagesContainer.appendChild(loadingElement);
		this.scrollToBottom(messagesContainer);
        
		return loadingElement;
	}

	/**
	 * Adds a collapsible reasoning element to messages.
	 *
	 * @param {string} reasoning - Reasoning text to display.
	 * @param {HTMLElement} messagesContainer - Container to add to.
	 * @return {HTMLElement} Reasoning element.
	 */
	addReasoningElement(reasoning, messagesContainer) {
		const reasoningElement = this.uiHelpers.createElement('div', 'agenticwp-reasoning-block');
        
		const reasoningToggle = this.uiHelpers.createElement('button', 'agenticwp-reasoning-toggle collapsed');
		reasoningToggle.innerHTML = '💭 <span>Show thinking...</span> <svg class="agenticwp-toggle-arrow" viewBox="0 0 24 24"><path d="M7 10l5 5 5-5z"/></svg>';
		reasoningToggle.setAttribute('aria-expanded', 'false');
		reasoningToggle.setAttribute('type', 'button');
		reasoningToggle.setAttribute('aria-label', 'Toggle reasoning display');
        
		const reasoningContent = this.uiHelpers.createElement('div', 'agenticwp-reasoning-text collapsed');
        
		if (this.uiHelpers.containsMarkdown(reasoning)) {
			reasoningContent.innerHTML = this.uiHelpers.renderMarkdown(reasoning);
		} else {
			reasoningContent.textContent = reasoning;
		}
        
		reasoningToggle.addEventListener('click', () => {
			const isExpanded = reasoningToggle.getAttribute('aria-expanded') === 'true';
			const newExpanded = !isExpanded;
            
			reasoningToggle.setAttribute('aria-expanded', newExpanded);
			reasoningContent.classList.toggle('collapsed', !newExpanded);
			reasoningToggle.classList.toggle('collapsed', !newExpanded);
		});
        
		reasoningElement.appendChild(reasoningToggle);
		reasoningElement.appendChild(reasoningContent);
        
		messagesContainer.appendChild(reasoningElement);
		this.scrollToBottom(messagesContainer);
        
		return reasoningElement;
	}

	/**
	 * Displays an error message in the chat.
	 *
	 * @param {string} message - Error message text.
	 * @param {HTMLElement} messagesContainer - Container to display error in.
	 */
	showError(message, messagesContainer) {
		const errorElement = this.uiHelpers.createElement('div', 'agenticwp-error-message', message);
        
		messagesContainer.appendChild(errorElement);
		this.scrollToBottom(messagesContainer);
        
		this.storage.autoRemove(errorElement);
	}

	/**
	 * Restores stored messages from local storage.
	 *
	 * @param {HTMLElement} messagesContainer - Container to restore messages to.
	 */
	restoreMessages(messagesContainer) {
		if (this.state.messages.length === 0) {
			this.addMessage('What are we working on today?', 'assistant', messagesContainer);
			return;
		}
        
		messagesContainer.innerHTML = '';
        
		this.state.messages.forEach(message => {
			if (message.type === 'assistant' && message.reasoning && message.reasoning.trim().length > 0) {
				this.addReasoningElement(message.reasoning, messagesContainer);
			}
            
			this.addMessage(message.content, message.type, messagesContainer, '', true);
		});
	}

	/**
	 * Clears chat history and resets conversation.
	 *
	 * @param {HTMLElement} messagesContainer - Container to clear.
	 */
	clearChatHistory(messagesContainer) {
		messagesContainer.innerHTML = '';
        
		this.state.messages = [];
		this.state.conversationId = null;
        
		this.storage.remove('agenticwp_messages');
		this.storage.remove('agenticwp_conversation_id');
        
		this.addMessage('What are we working on today?', 'assistant', messagesContainer);
	}

	/**
	 * Scrolls messages container to bottom.
	 *
	 * @param {HTMLElement} messagesContainer - Container to scroll.
	 */
	scrollToBottom(messagesContainer) {
		messagesContainer.scrollTop = messagesContainer.scrollHeight;
	}

	/**
	 * Delays execution for specified milliseconds.
	 *
	 * @param {number} ms - Milliseconds to wait.
	 * @return {Promise<void>}
	 */
	sleep(ms) {
		return new Promise(resolve => setTimeout(resolve, ms));
	}

	/**
	 * Gets stored conversation ID from storage.
	 *
	 * @return {string|null} Conversation ID or null.
	 */
	getStoredConversationId() {
		return this.storage.get('agenticwp_conversation_id');
	}

	/**
	 * Stores conversation ID in storage.
	 *
	 * @param {string} id - Conversation ID to store.
	 */
	storeConversationId(id) {
		if (id) {
			this.storage.set('agenticwp_conversation_id', id);
		}
	}

	/**
	 * Gets stored messages from storage.
	 *
	 * @return {Array} Array of stored messages.
	 */
	getStoredMessages() {
		return this.storage.get('agenticwp_messages', []);
	}

	/**
	 * Stores messages in storage with limit.
	 */
	storeMessages() {
		const messageLimit = agenticwp_ajax.settings?.message_history_limit || 50;
		const messagesToStore = this.state.messages.slice(-messageLimit);
		this.storage.set('agenticwp_messages', messagesToStore);
	}
}

window.ChatAPI = ChatAPI;