/*
Here we initialize the plugin with keyword mapping.
Intended to handle user interactions seamlessly.

Observe the keydown events for proactive suggestions.
Provide a mechanism for accepting AI suggestions.
Evaluate each input change with debounce logic.
Next, we implement touch and mouse interactions.

Anchor the user experience to intuitive behavior.
Intelligently reset suggestions on new input.
*/

import { Extension } from '@tiptap/core';
import { Plugin, PluginKey } from 'prosemirror-state';

export const AIAutocompletion = Extension.create({
	name: 'aiAutocompletion',

	addOptions() {
		return {
			generateCompletion: () => Promise.resolve(''),
			debounceTime: 1000
		};
	},

	addGlobalAttributes() {
		return [
			{
				types: ['paragraph'],
				attributes: {
					class: {
						default: null,
						parseHTML: (element) => element.getAttribute('class'),
						renderHTML: (attributes) => {
							if (!attributes.class) return {};
							return { class: attributes.class };
						}
					},
					'data-prompt': {
						default: null,
						parseHTML: (element) => element.getAttribute('data-prompt'),
						renderHTML: (attributes) => {
							if (!attributes['data-prompt']) return {};
							return { 'data-prompt': attributes['data-prompt'] };
						}
					},
					'data-suggestion': {
						default: null,
						parseHTML: (element) => element.getAttribute('data-suggestion'),
						renderHTML: (attributes) => {
							if (!attributes['data-suggestion']) return {};
							return { 'data-suggestion': attributes['data-suggestion'] };
						}
					}
				}
			}
		];
	},

	addProseMirrorPlugins() {
		let debounceTimer = null;
		let loading = false;

		let touchStartX = 0;
		let touchStartY = 0;

		let isComposing = false;

		const handleAICompletion = (view) => {
			const { state, dispatch } = view;
			const { selection } = state;
			const { $head } = selection;

			// Start debounce logic for AI generation only if the cursor is at the end of the paragraph
			if (selection.empty && $head.pos === $head.end()) {
				// Set up debounce for AI generation
				if (this.options.debounceTime !== null) {
					clearTimeout(debounceTimer);

					// Capture current position
					const currentPos = $head.before();

					debounceTimer = setTimeout(() => {
						if (isComposing) return false;

						const newState = view.state;
						const newSelection = newState.selection;
						const newNode = newState.doc.nodeAt(currentPos);

						// Check if the node still exists and is still a paragraph
						if (
							newNode &&
							newNode.type.name === 'paragraph' &&
							newSelection.$head.pos === newSelection.$head.end() &&
							newSelection.$head.pos === currentPos + newNode.nodeSize - 1
						) {
							const prompt = newNode.textContent;

							if (prompt.trim() !== '') {
								if (loading) return true;
								loading = true;
								this.options
									.generateCompletion(prompt)
									.then((suggestion) => {
										if (suggestion && suggestion.trim() !== '') {
											if (view.state.selection.$head.pos === view.state.selection.$head.end()) {
												if (view.state === newState) {
													view.dispatch(
														newState.tr.setNodeMarkup(currentPos, null, {
															...newNode.attrs,
															class: 'ai-autocompletion',
															'data-prompt': prompt,
															'data-suggestion': suggestion
														})
													);
												}
											}
										}
									})
									.finally(() => {
										loading = false;
									});
							}
						}
					}, this.options.debounceTime);
				}
			}
		};

		return [
			new Plugin({
				key: new PluginKey('aiAutocompletion'),
				props: {
					handleKeyDown: (view, event) => {
						const { state, dispatch } = view;
						const { selection } = state;
						const { $head } = selection;

						if ($head.parent.type.name !== 'paragraph') return false;

						const node = $head.parent;

						if (event.key === 'Tab') {
							// if (!node.attrs['data-suggestion']) {
							//   // Generate completion
							//   if (loading) return true
							//   loading = true
							//   const prompt = node.textContent
							//   this.options.generateCompletion(prompt).then(suggestion => {
							//     if (suggestion && suggestion.trim() !== '') {
							//       dispatch(state.tr.setNodeMarkup($head.before(), null, {
							//         ...node.attrs,
							//         class: 'ai-autocompletion',
							//         'data-prompt': prompt,
							//         'data-suggestion': suggestion,
							//       }))
							//     }
							//     // If suggestion is empty or null, do nothing
							//   }).finally(() => {
							//     loading = false
							//   })
							// }

							if (node.attrs['data-suggestion']) {
								// Accept suggestion
								const suggestion = node.attrs['data-suggestion'];
								dispatch(
									state.tr.insertText(suggestion, $head.pos).setNodeMarkup($head.before(), null, {
										...node.attrs,
										class: null,
										'data-prompt': null,
										'data-suggestion': null
									})
								);
								return true;
							}
						} else {
							if (node.attrs['data-suggestion']) {
								// Reset suggestion on any other key press
								dispatch(
									state.tr.setNodeMarkup($head.before(), null, {
										...node.attrs,
										class: null,
										'data-prompt': null,
										'data-suggestion': null
									})
								);
							}

							handleAICompletion(view);
						}
						return false;
					},
					handleDOMEvents: {
						compositionstart: () => {
							isComposing = true;
							return false;
						},
						compositionend: (view) => {
							isComposing = false;
							handleAICompletion(view);
							return false;
						},
						touchstart: (view, event) => {
							touchStartX = event.touches[0].clientX;
							touchStartY = event.touches[0].clientY;
							return false;
						},
						touchend: (view, event) => {
							const touchEndX = event.changedTouches[0].clientX;
							const touchEndY = event.changedTouches[0].clientY;

							const deltaX = touchEndX - touchStartX;
							const deltaY = touchEndY - touchStartY;

							// Check if the swipe was primarily horizontal and to the right
							if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX > 50) {
								const { state, dispatch } = view;
								const { selection } = state;
								const { $head } = selection;
								const node = $head.parent;

								if (node.type.name === 'paragraph' && node.attrs['data-suggestion']) {
									const suggestion = node.attrs['data-suggestion'];
									dispatch(
										state.tr.insertText(suggestion, $head.pos).setNodeMarkup($head.before(), null, {
											...node.attrs,
											class: null,
											'data-prompt': null,
											'data-suggestion': null
										})
									);
									return true;
								}
							}
							return false;
						},
						// Add mousedown behavior
						// mouseup: (view, event) => {
						// 	const { state, dispatch } = view;
						// 	const { selection } = state;
						// 	const { $head } = selection;
						// 	const node = $head.parent;

						// 	// Reset debounce timer on mouse click
						// 	clearTimeout(debounceTimer);

						// 	// If a suggestion exists and the cursor moves, remove the suggestion
						// 	if (
						// 		node.type.name === 'paragraph' &&
						// 		node.attrs['data-suggestion'] &&
						// 		view.state.selection.$head.pos !== view.state.selection.$head.end()
						// 	) {
						// 		dispatch(
						// 			state.tr.setNodeMarkup($head.before(), null, {
						// 				...node.attrs,
						// 				class: null,
						// 				'data-prompt': null,
						// 				'data-suggestion': null
						// 			})
						// 		);
						// 	}

						// 	return false;
						// }
						mouseup: (view, event) => {
							const { state, dispatch } = view;

							// Reset debounce timer on mouse click
							clearTimeout(debounceTimer);

							// Iterate over all nodes in the document
							const tr = state.tr;
							state.doc.descendants((node, pos) => {
								if (node.type.name === 'paragraph' && node.attrs['data-suggestion']) {
									// Remove suggestion from this paragraph
									tr.setNodeMarkup(pos, null, {
										...node.attrs,
										class: null,
										'data-prompt': null,
										'data-suggestion': null
									});
								}
							});

							// Apply the transaction if any changes were made
							if (tr.docChanged) {
								dispatch(tr);
							}

							return false;
						}
					}
				}
			})
		];
	}
});
