window.aiAssistantOptions = window.aiAssistantOptions || {};
if (typeof aiAssistantOptions === 'undefined') { var aiAssistantOptions = function(){ return (window.VvvebAi && VvvebAi.options) ? VvvebAi.options() : (window.aiAssistantOptions || {}); }; }
window.addEventListener("tinymce.options", function (event) {
	let tinyMceOptions = event.detail;
	if (tinyMceOptions.quickbars_insert_toolbar && !tinyMceOptions.quickbars_insert_toolbar.includes('AiAssistant')) tinyMceOptions.quickbars_insert_toolbar += '| AiAssistant';
	if (tinyMceOptions.toolbar && !tinyMceOptions.toolbar.includes('AiAssistant')) tinyMceOptions.toolbar += '| AiAssistant';

	return tinyMceOptions;
});

window.addEventListener("tinymce.setup", function (event) {
	let editor = event.detail;
	window.tinyMceInstance = editor;

	function openCustomPrompt() {
		if (!aiAssistantEnsureKey()) return;
		let selection = aiAssistantGetSelection();
		editor.windowManager.open({
			title: 'Ask AI — custom prompt',
			body: {
				type: 'panel',
				items: [
					{type: 'textarea', name: 'instruction', label: 'Instruction'}
				]
			},
			buttons: [
				{type: 'cancel', text: 'Close'},
				{type: 'submit', text: 'Generate', primary: true}
			],
			initialData: {instruction: selection.text || ''},
			onSubmit: (api) => {
				let instruction = (api.getData().instruction || '').trim();
				api.close();
				if (!instruction) {
					displayToast("bg-warning", "Warning", "Enter an instruction first.");
					return;
				}
				let source = selection.html || selection.text;
				let prompt = source ? instruction + "\n\n" + source : instruction;
				aiAssistantSendQuery(prompt, {placement: source ? 'replace' : 'insert'});
			}
		});
	}

	function openImagePrompt() {
		if (!aiAssistantEnsureKey()) return;
		editor.windowManager.open({
			title: 'Generate image from text',
			body: {
				type: 'panel',
				items: [
					{type: 'textarea', name: 'description', label: 'Image description'}
				]
			},
			buttons: [
				{type: 'cancel', text: 'Close'},
				{type: 'submit', text: 'Generate', primary: true}
			],
			initialData: {description: (aiAssistantGetSelection().text || '')},
			onSubmit: (api) => {
				let desc = (api.getData().description || '').trim();
				api.close();
				if (!desc) {
					displayToast("bg-warning", "Warning", "Describe the image to generate first.");
					return;
				}
				aiAssistantGenerateImage(desc);
			}
		});
	}

	// custom AI icons in ionicons outline style (512 grid, stroke 32, round caps)
	editor.ui.registry.addIcon('ai-spark', '<svg width="24" height="24" viewBox="0 0 512 512" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"><path fill="none" d="M256 64l48 144 144 48-144 48-48 144-48-144-144-48 144-48z"/><path fill="none" d="M408 336l18 54 54 18-54 18-18 54-18-54-54-18 54-18z"/></svg>');
	editor.ui.registry.addIcon('ai-continue', '<svg width="24" height="24" viewBox="0 0 512 512" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"><path fill="none" d="M336 112l96 96L240 400l-112 32 32-112z"/><path fill="none" d="M304 144l96 96"/></svg>');
	editor.ui.registry.addIcon('ai-summarize', '<svg width="24" height="24" viewBox="0 0 512 512" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"><path fill="none" d="M176 144h288M176 256h288M176 368h288"/><circle fill="none" cx="96" cy="144" r="24"/><circle fill="none" cx="96" cy="256" r="24"/><circle fill="none" cx="96" cy="368" r="24"/></svg>');
	editor.ui.registry.addIcon('ai-article', '<svg width="24" height="24" viewBox="0 0 512 512" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"><path fill="none" d="M128 80h160l96 96v240H128z"/><path fill="none" d="M288 80v96h96"/><path fill="none" d="M168 272h176M168 320h176"/></svg>');
	editor.ui.registry.addIcon('ai-image', '<svg width="24" height="24" viewBox="0 0 512 512" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"><path fill="none" d="M64 96h384v320H64z"/><circle fill="none" cx="168" cy="208" r="32"/><path fill="none" d="M64 360l112-112 88 88 64-64 120 120"/></svg>');
	editor.ui.registry.addIcon('ai-grammar', '<svg width="24" height="24" viewBox="0 0 512 512" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"><path fill="none" d="M80 144h240M80 240h240M80 336h144"/><path fill="none" d="m288 368 32 32 80-80"/></svg>');
	editor.ui.registry.addIcon('ai-custom', '<svg width="24" height="24" viewBox="0 0 512 512" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"><path fill="none" d="M400 96H112a48 48 0 00-48 48v176a48 48 0 0048 48h48v80l96-80h144a48 48 0 0048-48V144a48 48 0 00-48-48z"/></svg>');

	editor.ui.registry.addMenuButton('AiAssistant', {
		text: "Ask AI",
		icon: 'ai-spark',
		tooltip: 'AI writing tools',
		fetch: (callback) => {
			callback([
				{type: 'menuitem', text: 'Keep writing', icon: 'ai-continue', onAction: () => aiAssistantAction('keep-writing')},
				{type: 'menuitem', text: 'Summarize', icon: 'ai-summarize', onAction: () => aiAssistantAction('summarize')},
				{type: 'menuitem', text: 'Write article from text', icon: 'ai-article', onAction: () => aiAssistantAction('write-article')},
				{type: 'menuitem', text: 'Generate image from text', icon: 'ai-image', onAction: () => aiAssistantAction('generate-image')},
				{type: 'menuitem', text: 'Fix grammar', icon: 'ai-grammar', onAction: () => aiAssistantAction('fix-grammar')},
				{type: 'separator'},
				{type: 'menuitem', text: 'Custom prompt…', icon: 'ai-custom', onAction: () => openCustomPrompt()},
			]);
		}
	});


	// expose dialog openers for external callers
	editor.aiAssistantCustomPrompt = openCustomPrompt;
	editor.aiAssistantImagePrompt = openImagePrompt;
});

function aiAssistantGetEditor() {
	return (typeof tinymce !== 'undefined' && tinymce.activeEditor) ? tinymce.activeEditor : null;
}

function aiAssistantGetSelection() {
	let ed = aiAssistantGetEditor();
	if (!ed) return {html: "", text: ""};
	return {html: ed.selection.getContent() || "", text: (ed.selection.getContent({format: 'text'}) || "").trim()};
}

function aiAssistantEnsureKey() {
	let opts = (typeof VvvebAi !== 'undefined' && VvvebAi.options) ? VvvebAi.options() : {};
	if (opts.apiMode !== "proxy" && !opts.key) {
		displayToast("bg-danger", "Error", 'No AI API key configured! Enter a valid key in the plugin settings page.');
		return false;
	}
	return true;
}

function aiAssistantImagesUrl() {
	let opts = (typeof VvvebAi !== 'undefined' && VvvebAi.options) ? VvvebAi.options() : {};
	let base = opts.proxyUrl || window.location.pathname + "?module=plugins/ai-assistant/proxy";
	return base + (base.includes('?') ? '&' : '?') + 'action=images';
}

function aiAssistantExtractTitle(text, html) {
	if (html) {
		let m = String(html).match(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/i);
		if (m) {
			let t = m[1].replace(/<[^>]+>/g, '').trim();
			if (t) return t.slice(0, 140);
		}
	}
	let lines = String(text || '').split('\n');
	for (let l of lines) {
		let mm = l.match(/^\s*#{1,6}\s+(.*)/);
		if (mm && mm[1].trim()) return mm[1].trim().slice(0, 140);
	}
	for (let l of lines) {
		let s = l.replace(/<[^>]+>/g, '').trim();
		if (s) return s.slice(0, 140);
	}
	return '';
}

function aiAssistantMaybeUpdateTitle(source, isHtml) {
	let input = document.querySelector('input[data-v-post-content-name]');
	if (!input) return;
	let title = isHtml ? aiAssistantExtractTitle('', source) : aiAssistantExtractTitle(source, '');
	if (!title && !isHtml) title = aiAssistantExtractTitle('', source);
	if (!title || input.value === title) return;
	input.value = title;
	try { input.dispatchEvent(new Event('input', {bubbles: true})); } catch(e){}
	try { input.dispatchEvent(new Event('change', {bubbles: true})); } catch(e){}
}

function aiAssistantInsertHtml(html) {
	let ed = aiAssistantGetEditor();
	if (ed) ed.insertContent(html);
}

function aiAssistantReplaceSelection(html) {
	let ed = aiAssistantGetEditor();
	if (ed) ed.selection.setContent(html);
}

function aiAssistantRenderHtml(text) {
	let raw = String(text || '');
	if (!raw.trim()) return '';
	// prefer extractHtmlFromMarkdown: keeps model HTML as-is (fix-grammar/summarize
	// return HTML tags) instead of escaping them like markdownToHtml does
	if (typeof VvvebAi !== 'undefined' && VvvebAi.extractHtmlFromMarkdown) {
		try {
			let html = VvvebAi.extractHtmlFromMarkdown(raw);
			if (html) return html;
		} catch(e){}
	}
	if (typeof VvvebAi !== 'undefined' && VvvebAi.markdownToHtml) {
		try {
			let html = VvvebAi.markdownToHtml(raw);
			if (html) return html;
		} catch(e){}
	}
	// fallback: escaped text with line breaks
	let esc = raw.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
	return '<p>' + esc.replace(/\n{2,}/g, '</p><p>').replace(/\n/g, '<br>') + '</p>';
}

let aiAssistantStreamSeq = 0;

async function aiAssistantAction(action) {
	if (!aiAssistantEnsureKey()) return;
	let ed = aiAssistantGetEditor();
	if (!ed) {
		displayToast("bg-warning", "Warning", "Open an editor first.");
		return;
	}
	if (action === 'generate-image') {
		let prompt = (aiAssistantGetSelection().text || "").trim();
		if (!prompt) {
			if (ed.aiAssistantImagePrompt) ed.aiAssistantImagePrompt();
			else displayToast("bg-warning", "Warning", "Select or describe the image to generate first.");
			return;
		}
		return aiAssistantGenerateImage(prompt);
	}
	let prompts = {
		'keep-writing': {system: 'You are a writing assistant. Continue the text naturally in the same tone, language and formatting.', instruction: 'Continue writing from where the following text ends. Keep the same tone, language and formatting (including HTML tags if present). Return only the continuation, no preamble:', placement: 'insert'},
		'summarize': {system: 'You summarize text concisely. Return clean HTML.', instruction: 'Summarize the following text concisely in the same language. Return a short HTML paragraph or list, no preamble:', placement: 'replace'},
		'write-article': {system: 'You are an expert article writer. Write complete, well-structured articles in clean HTML.', instruction: 'Write a complete, well-structured article based on the following text. Include a headline (h2), an engaging introduction, organized body sections with subheadings (h3), and a conclusion. Expand on the ideas, keep the same language, return only clean HTML with no preamble:', placement: 'replace'},
		'fix-grammar': {system: 'You are a proofreader. Fix grammar, spelling and punctuation, preserve HTML tags.', instruction: 'Fix grammar, spelling and punctuation in the following text. Return only the corrected text, preserving HTML tags and formatting, no explanations:', placement: 'replace'}
	};
	let cfg = prompts[action];
	if (!cfg) return;
	let selection = aiAssistantGetSelection();
	let source = selection.html || selection.text;
	if (!source.trim()) {
		displayToast("bg-warning", "Warning", "Select some text first.");
		return;
	}
	return aiAssistantSendQuery(cfg.instruction + "\n\n" + source, {system: cfg.system, action, placement: cfg.placement});
}

async function aiAssistantFetchImageUrl(promptText) {
	promptText = (promptText || "").trim();
	if (!promptText) throw new Error('Describe the image to generate first.');
	let opts = (typeof VvvebAi !== 'undefined' && VvvebAi.options) ? VvvebAi.options() : {};
	if (opts.apiMode !== "proxy" && !opts.key) throw new Error('No AI API key configured! Enter a valid key in the plugin settings page.');
	let imageUrl = "";
	if (opts.apiMode === "proxy") {
		let res = await fetch(aiAssistantImagesUrl(), {
			method: 'POST',
			headers: {'Content-Type': 'application/json'},
			body: JSON.stringify({provider: opts.provider, model: opts.imageModel, size: opts.imageSize, prompt: promptText, csrf: opts.csrf})
		});
		let data = await res.json().catch(() => ({}));
		if (!res.ok) throw new Error((data.error && (data.error.message || data.error)) || ('Image generation failed (' + res.status + ')'));
		imageUrl = (data.data && data.data[0] && (data.data[0].url || data.data[0].b64_json)) || "";
	} else {
		let res = await fetch("https://api.openai.com/v1/images/generations", {
			method: 'POST',
			headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + opts.key},
			body: JSON.stringify({model: opts.imageModel || 'dall-e-3', prompt: promptText, size: opts.imageSize || '1024x1024', n: 1})
		});
		let data = await res.json().catch(() => ({}));
		if (!res.ok) throw new Error((data.error && data.error.message) || ('Image generation failed (' + res.status + ')'));
		imageUrl = (data.data && data.data[0] && (data.data[0].url || data.data[0].b64_json)) || "";
	}
	if (!imageUrl) throw new Error('No image returned by the provider.');
	if (!imageUrl.startsWith('http') && !imageUrl.startsWith('data:')) imageUrl = 'data:image/png;base64,' + imageUrl;
	return imageUrl;
}

async function aiAssistantGenerateImage(promptText) {
	promptText = (promptText || "").trim();
	if (!promptText) {
		displayToast("bg-warning", "Warning", "Describe the image to generate first.");
		return;
	}
	let ed = aiAssistantGetEditor();
	if (ed) ed.setProgressState(true);
	try {
		let imageUrl = await aiAssistantFetchImageUrl(promptText);
		let safePrompt = promptText.replace(/"/g, '&quot;').slice(0, 200);
		aiAssistantInsertHtml('<img src="' + imageUrl.replace(/"/g, '') + '" alt="' + safePrompt + '" style="max-width:100%">');
	} catch (error) {
		console.log("image generation failed", error);
		displayToast("bg-danger", "Error", error.message || String(error));
	} finally {
		if (ed) ed.setProgressState(false);
	}
}

function aiAssistantGetArticleText(maxChars) {
	maxChars = maxChars || 2000;
	let text = "";
	let ed = aiAssistantGetEditor();
	if (ed) {
		try { text = ed.getContent({format: 'text'}) || ""; } catch(e){}
	}
	if (!text.trim()) {
		let ta = document.querySelector('textarea[data-v-post-content-content], textarea[data-v-product-content-content]');
		if (ta && ta.value) text = ta.value.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ');
	}
	text = (text || "").trim().replace(/\s+/g, ' ');
	if (text.length > maxChars) text = text.slice(0, maxChars) + '…';
	return text;
}

function aiAssistantSetFeaturedImage(url) {
	let input = document.querySelector('input#featured-image-input');
	let thumb = document.querySelector('img#featured-image-thumb');
	if (input) {
		input.value = url;
		try { input.dispatchEvent(new Event('input', {bubbles: true})); } catch(e){}
		try { input.dispatchEvent(new Event('change', {bubbles: true})); } catch(e){}
	}
	if (thumb) thumb.setAttribute('src', url);
}

function aiAssistantOpenFeaturedImageDialog() {
	if (!aiAssistantEnsureKey()) return;
	let articleText = aiAssistantGetArticleText();
	if (typeof bootstrap !== 'undefined' && bootstrap.Modal) {
		let modalEl = document.getElementById('ai-featured-image-modal');
		if (!modalEl) {
			modalEl = document.createElement('div');
			modalEl.id = 'ai-featured-image-modal';
			modalEl.className = 'modal fade';
			modalEl.tabIndex = -1;
			modalEl.innerHTML = '<div class="modal-dialog"><div class="modal-content">'
				+ '<div class="modal-header"><p class="modal-title text-primary"><i class="la la-magic"></i> Generate featured image</p>'
				+ '<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button></div>'
				+ '<div class="modal-body"><textarea rows="5" class="form-control" id="ai-featured-image-prompt" placeholder="Describe the image to generate…"></textarea>'
				+ '<div class="form-text">Prefilled with the article text — edit it to describe the image.</div></div>'
				+ '<div class="modal-footer"><span class="spinner-border spinner-border-sm me-auto" role="status" style="display:none"></span>'
				+ '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>'
				+ '<button type="button" class="btn btn-success" id="ai-featured-image-generate"><i class="la la-magic"></i> Generate</button></div>'
				+ '</div></div>';
			document.body.append(modalEl);
			modalEl.querySelector('#ai-featured-image-generate').addEventListener('click', async () => {
				let prompt = modalEl.querySelector('#ai-featured-image-prompt').value.trim();
				if (!prompt) { displayToast("bg-warning", "Warning", "Describe the image to generate first."); return; }
				let btn = modalEl.querySelector('#ai-featured-image-generate');
				let spinner = modalEl.querySelector('.spinner-border');
				btn.disabled = true;
				spinner.style.display = '';
				try {
					let url = await aiAssistantFetchImageUrl(prompt);
					aiAssistantSetFeaturedImage(url);
					bootstrap.Modal.getInstance(modalEl).hide();
					displayToast("bg-success", "Success", "Featured image generated.");
				} catch (error) {
					console.log("featured image generation failed", error);
					displayToast("bg-danger", "Error", error.message || String(error));
				} finally {
					btn.disabled = false;
					spinner.style.display = 'none';
				}
			});
		}
		modalEl.querySelector('#ai-featured-image-prompt').value = articleText;
		bootstrap.Modal.getOrCreateInstance(modalEl).show();
	} else if (articleText || true) {
		let prompt = window.prompt('Describe the image to generate:', articleText);
		if (!prompt || !prompt.trim()) return;
		aiAssistantFetchImageUrl(prompt.trim()).then(aiAssistantSetFeaturedImage).catch((error) => {
			displayToast("bg-danger", "Error", error.message || String(error));
		});
	}
}

document.addEventListener('click', function(event) {
	let btn = event.target.closest('[data-ai-generate-featured-image]');
	if (!btn) return;
	event.preventDefault();
	aiAssistantOpenFeaturedImageDialog();
});

async function aiAssistantSendQuery(promptText, opts)  {
		opts = opts || {};
		const options = (typeof VvvebAi !== 'undefined' && VvvebAi.options) ? VvvebAi.options() : aiAssistantOptions();

		// key is only needed for direct provider calls; proxy mode keeps keys server side
		if (options.apiMode !== "proxy" && !options.key) {
			displayToast("bg-danger", "Error", 'No AI API key configured! Enter a valid key in the plugin settings page.');
			return;
		}

		let ed = aiAssistantGetEditor();
		if (!ed) {
			displayToast("bg-warning", "Warning", "Open an editor first.");
			return;
		}
		ed.focus();

		let text = (typeof promptText === 'string' && promptText) ? promptText : '';
		if (!text.trim()) {
			displayToast("bg-warning", "Warning", "Nothing to send.");
			return;
		}

		let placement = opts.placement || 'insert';
		let streamId = 'ai-stream-' + (++aiAssistantStreamSeq) + '-' + Date.now().toString(36);

		// prepare target: replace deletes the selection, insert collapses to end.
		// insert the placeholder via Range API (not insertContent) so tinymce does
		// not strip its id and the node stays queryable while streaming.
		let streamEl = null;
		ed.undoManager.transact(() => {
			if (placement === 'replace') {
				if (!ed.selection.isCollapsed()) ed.selection.setContent('');
			} else {
				ed.selection.collapse(false);
			}
			let rng = ed.selection.getRng();
			let doc = ed.getDoc();
			let span = doc.createElement('div');
			span.setAttribute('id', streamId);
			span.setAttribute('data-ai-stream', '1');
			rng.insertNode(span);
			rng.setStartAfter(span);
			rng.collapse(true);
			ed.selection.setRng(rng);
			streamEl = span;
		});

		function streamNode() {
			if (streamEl && streamEl.isConnected) return streamEl;
			try { streamEl = ed.getBody().querySelector('#' + CSS.escape(streamId)); } catch(e){ streamEl = null; }
			return streamEl;
		}

		let full = '';
		let lastScroll = 0;
		function renderStream() {
			let node = streamNode();
			if (!node) return;
			// model returns HTML for these actions - render it live so tags
			// don't show as code while streaming (browser auto-closes partial tags)
			try { node.innerHTML = full; } catch(e){ node.textContent = full; }
			// live post title update for article generation
			if (opts.action === 'write-article') aiAssistantMaybeUpdateTitle(full, false);
			// keep streamed content visible: scroll the editor window as it grows
			let now = Date.now();
			if (now - lastScroll > 120) {
				lastScroll = now;
				try {
					let win = ed.getWin();
					let rect = node.getBoundingClientRect();
					let winH = win.innerHeight || 400;
					if (rect.bottom > winH - 60) win.scrollTo(win.scrollX, win.scrollY + (rect.bottom - winH + 60));
				} catch(e){}
				try { ed.nodeChanged(); } catch(e){}
			}
		}

		const payload = VvvebAi.buildPayload({
			text: text,
			system: opts.system || options.systemPrompt,
			provider: options.provider,
			model: options.model,
			temperature: options.temperature,
			maxTokens: options.maxTokens,
			contextSize: options.contextSize,
			options: {
				key: options.key,
				url: options.url,
				model: options.model,
				temperature: options.temperature,
				max_tokens: options.maxTokens,
				contextSize: options.contextSize,
				apiMode: options.apiMode,
				proxyUrl: options.proxyUrl || window.location.pathname + "?module=plugins/ai-assistant/proxy",
				proxyCsrf: options.csrf
			}
		});

		ed.setProgressState(true);
		try {
			const result = await VvvebAi.sendQuery(payload, (event) => {
				if (event.type === 'text-delta' && event.text) {
					full += event.text;
					renderStream();
				}
			});

			if (!full && result.text) full = result.text;
			if (result.thinking && !full) full = result.thinking;

			ed.undoManager.transact(() => {
				let node = streamNode();
				let html = aiAssistantRenderHtml(full) || '<em>No content returned.</em>';
				if (node) {
					let tmp = ed.getDoc().createElement('div');
					tmp.innerHTML = html;
					let parent = node.parentNode;
					while (tmp.firstChild) parent.insertBefore(tmp.firstChild, node);
					parent.removeChild(node);
					streamEl = null;
				} else if (full) {
					ed.insertContent(aiAssistantRenderHtml(full));
				}
				if (opts.action === 'write-article') aiAssistantMaybeUpdateTitle(html, true);
			});

			// surface tool calls so plugins can execute them and send results back
			if ((result.toolCalls || []).length) {
				window.aiAssistantLastPayload = Object.assign(payload, {_lastToolCalls: result.toolCalls, _lastText: result.text || full, _lastThinking: result.thinking || ''});
				document.dispatchEvent(new CustomEvent("vvveb-ai-tool-calls", {detail: {calls: result.toolCalls, payload, result}}));
			}

			return result;
		} catch (error) {
			console.log("something went wrong", error);
			ed.undoManager.transact(() => {
				let node = streamNode();
				if (node && node.parentNode) node.parentNode.removeChild(node);
			});
			displayToast("bg-danger", "Error", error.message || String(error));
		} finally {
			ed.setProgressState(false);
		}
}
