// AGPL-3.0-only
//
// This is free software for the public good of a permacomputer
// hosted at permacomputer.com, an always-on computer by the people, for the
// people. One which is durable, easy to repair, and distributed like tap water
// for machine learning intelligence.
//
// The permacomputer is community-owned infrastructure optimized around
// four values:
//
//   TRUTH    - First principles, math & science, open source code
//   FREEDOM  - Voluntary partnerships, freedom from tyranny & corporate control
//   HARMONY  - Minimal waste, self-renewing systems with diverse connections
//   LOVE     - Be yourself without hurting others, cooperation through natural law
//
// This software contributes to that vision by making machine learning
// accessible to everyone through a free, open, embeddable chat interface.
// Code is seeds to sprout on any abandoned technology.
//
// Learn more: https://www.permacomputer.com
//
// NO WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
//
// Copyright 2025-2026 TimeHexOn & foxhop & russell@unturf
// https://www.permacomputer.com

// UI creation and management functionality
import { marked } from "https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js";
import hljs from "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/es/highlight.min.js";
import { API_KEY, setSystemMessageAppend, TTS_API_URL, getAPIConfig } from "./config.js";
import { extractWebpageContent } from "./content.js";
import { detectPageLanguage } from "./language-detection.js";
import { speakText } from "./tts.js";
import {
	detectCurrentTheme,
	getThemeColors,
	initializeChunkFiveFont,
} from "./ui-themes.js";
import {
	getUIText,
	getUserLanguagePreference,
	setUserLanguagePreference,
} from "./ui-translations.js";
import { NATIVE_LANGUAGE_NAMES } from "./translation.js";
import { openTTSModal } from "./tts-modal.js";
import { openTranslateModal } from "./translate-modal.js";
import {
	createFullInterface,
	createCustomInterface,
	createButton,
	handleTTSFromElement,
	handleUploadFromElement,
	handleSmartTranslate,
} from "./widget-library.js";
import {
	openUncloseaiEmbeddedModal,
	openUncloseaiEmbeddedModalNew,
} from "./uncloseai-embed-modal.js";

// Refresh UI elements when language preference changes
function refreshUILanguage() {
	console.log("UI language refresh triggered");

	// Update existing button texts if modal is open
	const readBtn = document.querySelector('[onclick*="readPageWithHermes"]');
	if (readBtn) readBtn.textContent = getUIText("readPage");

	const ttsBtn = document.querySelector('[onclick*="openTTSModal"]');
	if (ttsBtn) ttsBtn.textContent = getUIText("ttsAnything");

	const translateBtn = document.querySelector(
		'[onclick*="openTranslateModal"]',
	);
	if (translateBtn) translateBtn.textContent = getUIText("translate");

	// Update smart translate buttons
	const smartTranslateBtns = document.querySelectorAll(
		'[onclick*="handleSmartTranslate"]',
	);
	smartTranslateBtns.forEach(
		(btn) => (btn.textContent = getUIText("smartTranslate")),
	);

	// Update labels if settings panel is open
	const modelLabel = document.querySelector(
		"#hermes-model-select",
	)?.previousElementSibling;
	if (modelLabel) modelLabel.textContent = getUIText("modelLabel");

	const voiceLabel = document.querySelector(
		"#hermes-voice-select",
	)?.previousElementSibling;
	if (voiceLabel) voiceLabel.textContent = getUIText("voiceLabel");

	const langLabel = document.querySelector(
		"#hermes-language-select",
	)?.previousElementSibling;
	if (langLabel) langLabel.textContent = getUIText("languageLabel");
}

// Ensure font is loaded when module is imported
if (typeof document !== "undefined") {
	initializeChunkFiveFont();
}

// Helper function to add copy buttons to code blocks
function addCodeBlockCopyButtons(element) {
	const codeBlocks = element.querySelectorAll("pre code");
	codeBlocks.forEach((codeBlock) => {
		const pre = codeBlock.parentElement;
		if (pre.tagName.toLowerCase() === "pre") {
			// Make the pre element relative for positioning
			pre.style.position = "relative";

			// Create copy button
			const copyBtn = document.createElement("button");
			copyBtn.textContent = "📋";
			copyBtn.title = "Copy code";
			copyBtn.className = "uncloseai-code-copy-btn";

			copyBtn.onclick = async (e) => {
				e.stopPropagation();
				try {
					const codeText = codeBlock.textContent;
					await navigator.clipboard.writeText(codeText);
					const originalText = copyBtn.textContent;
					copyBtn.textContent = "✓";
					setTimeout(() => {
						copyBtn.textContent = originalText;
					}, 1000);
				} catch (error) {
					alert(getUIText("failedToCopyCode", { error: error.message }));
				}
			};

			pre.appendChild(copyBtn);
		}
	});
}

// Send message with custom history (for intro generation)
async function* sendMessageWithCustomHistory(messageHistory) {
	// Get API configuration (custom or default)
	const apiConfig = await getAPIConfig();
	
	let apiUrl, headers, model;
	
	if (apiConfig.isCustom) {
		// Use custom API configuration
		apiUrl = `${apiConfig.endpoint}/chat/completions`;
		headers = apiConfig.headers;
		model = apiConfig.model;
	} else {
		// Use default Hermes configuration
		const { getSelectedModel, getSelectedModelEndpoint } = await import(
			"./models.js"
		);
		apiUrl = `${getSelectedModelEndpoint()}/chat/completions`;
		headers = {
			"Content-Type": "application/json",
			Authorization: `Bearer ${API_KEY}`,
		};
		model = getSelectedModel();
	}

	// Import getSelectedModelMaxTokens to get dynamic max tokens and calculate available
	const { getSelectedModelMaxTokens, getSelectedModelMaxCompletionTokens } = await import("./models.js");
	const modelMaxTokens = getSelectedModelMaxTokens();
	
	// Estimate input tokens for intro generation
	const inputText = messageHistory.map(msg => msg.content).join('');
	const inputTokens = Math.ceil(inputText.length / 4);
	
	// For intro generation, be more generous - we want complete intros
	// With large context models, we have plenty of room
	const calculatedAvailable = Math.max(500, modelMaxTokens - inputTokens - 1000);
	
	// Cap to model's max completion tokens limit
	const maxCompletionTokens = getSelectedModelMaxCompletionTokens();
	// For intro generation, use the full available tokens up to the model's completion limit
	const finalAvailable = Math.min(calculatedAvailable, maxCompletionTokens);
	console.log("UI (intro generation) using tokens:", finalAvailable, "calculated:", calculatedAvailable, "max completion:", maxCompletionTokens, "of model max:", modelMaxTokens);
	
	const response = await fetch(apiUrl, {
		method: "POST",
		headers: headers,
		body: JSON.stringify({
			model: model,
			messages: messageHistory,
			temperature: 0.3,
			max_tokens: finalAvailable,
			stream: true,
		}),
	});

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

	const reader = response.body.getReader();
	const decoder = new TextDecoder();
	let streamBuffer = "";

	while (true) {
		const { done, value } = await reader.read();
		if (done) break;

		streamBuffer += decoder.decode(value, { stream: true });
		const lines = streamBuffer.split("\n");

		for (let i = 0; i < lines.length - 1; i++) {
			const line = lines[i].trim();
			if (line.startsWith("data: ")) {
				const jsonData = line.slice(6);
				if (jsonData === "[DONE]") continue;

				try {
					const parsedData = JSON.parse(jsonData);
					const content = parsedData.choices[0].delta.content;
					if (content) {
						yield content;
					}
				} catch (error) {
					console.error("Error parsing JSON:", error);
				}
			}
		}

		streamBuffer = lines[lines.length - 1];
	}
}

// Simple TTS function for chat messages (no preprocessing needed)
async function speakChatText(text, voice = "alloy", rate = 1.0) {
	try {
		const response = await fetch(TTS_API_URL, {
			method: "POST",
			headers: {
				"Content-Type": "application/json",
				Authorization: `Bearer ${API_KEY}`,
			},
			body: JSON.stringify({
				model: "tts-1-qwen",
				voice: voice,
				input: text,
			}),
		});

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

		const audioBlob = await response.blob();
		const audioUrl = URL.createObjectURL(audioBlob);
		const audio = new Audio(audioUrl);
		audio.playbackRate = rate;

		return { audio, blob: audioBlob };
	} catch (error) {
		console.error("Error in chat TTS:", error);
		throw error;
	}
}

import { chatHistory, handleUserInput, sendMessage } from "./chat.js";
import {
	addFileUploadButton,
	handleFileUpload,
	hideProgressIndicator,
	showProgressIndicator,
	uploadFile,
} from "./file-upload.js";
import {
	fetchModelsFromEndpoints,
	getSelectedModel,
	getSelectedModelEndpoint,
	modelRegistry,
} from "./models.js";
import { readPageWithHermes } from "./page-reader.js";
import {
	clearConversationHistory,
	getPageSpecificKey,
	loadConversationHistory,
	saveConversationHistory,
} from "./storage.js";
import {
	SUPPORTED_LANGUAGES,
	translateCurrentPage,
	translateText,
} from "./translation.js";

// Configuration flags
const USE_CUSTOM_STYLING = window.UNCLOSEAI_CUSTOM_STYLING !== false;
const SHOW_FLOATING_BUTTON = window.UNCLOSEAI_FLOATING_BUTTON !== false;

// Global variable to track modal state
let uncloseaiEmbeddedModalOpen = false;

// Initialize uncloseai elements based on class
export function initializeUncloseaiElements() {
	const uncloseaiElements = document.querySelectorAll(".uncloseai");

	uncloseaiElements.forEach((element) => {
		const features = element.dataset.features || "full";
		const type = element.dataset.type || "standard";

		// Create container for this uncloseai instance
		const container = document.createElement("div");
		container.className = "uncloseai-container uncloseai-ui-container";

		if (features === "full" || type === "full") {
			createFullInterface(container);
		} else {
			createCustomInterface(container, features.split(","));
		}

		element.appendChild(container);
	});
}

// Widget creation functions imported from widget-library.js

// Initialize the legacy chat interface (for backward compatibility)
export async function initializeChatInterface() {
	// Only initialize if there are legacy elements (chat-container, user-input, etc.)
	const legacyElements = document.querySelector(
		"#chat-container, #user-input, #chat-box",
	);
	if (!legacyElements) return;

	const pageContent = await extractWebpageContent();
	chatHistory.push({
		role: "system",
		content: `Here's the content of the webpage: ${pageContent}`,
	});

	marked.setOptions({
		highlight: (code, lang) => {
			const language = hljs.getLanguage(lang) ? lang : "plaintext";
			return hljs.highlight(code, { language }).value;
		},
	});

	// Add the Read Page button
	addReadPageButton();

	// Add the File Upload button and picker
	addFileUploadButton();

	// Event listeners
	document
		.getElementById("user-input")
		?.addEventListener("keydown", (event) => {
			if (event.key === "Enter" && !event.shiftKey) {
				event.preventDefault();
				handleUserInput();
			}
		});
}

// Add button to page
export function addReadPageButton() {
	const button = document.createElement("button");
	button.textContent = "Read Page";
	button.onclick = (event) => readPageWithHermes(event.target);
	button.className = "uncloseai-ui-button-margin";

	const ttsButton = document.createElement("button");
	ttsButton.textContent = "TTS Anything";
	ttsButton.onclick = openTTSModal;
	ttsButton.className = "uncloseai-ui-button-margin";

	document.body.appendChild(button);
	document.body.appendChild(ttsButton);
}

// File upload functionality moved to file-upload.js

// Helper function to create and append the floating button
function createAndAppendFloatingButton() {
	console.log("uncloseai.js: createAndAppendFloatingButton() called.");
	
	// Ensure ChunkFive font is loaded
	initializeChunkFiveFont();

	// Remove any existing floating button first
	const existingButton = document.getElementById("floating-ai-button");
	if (existingButton) {
		console.log("uncloseai.js: Found existing floating button, removing it.");
		existingButton.remove();
	}

	// Create the main floating button
	const floatingButton = document.createElement("button");
	floatingButton.id = "floating-ai-button";
	floatingButton.textContent = "uncloseai.";
	console.log("uncloseai.js: Created new floating button element.");

	// Function to update button theme
	function updateButtonTheme() {
		const isDark =
			document.documentElement.getAttribute("data-theme") === "dark" ||
			(window.matchMedia?.("(prefers-color-scheme: dark)").matches &&
				!document.documentElement.getAttribute("data-theme"));

		// Set floating button theme colors via CSS custom properties
		const root = document.documentElement;
		root.style.setProperty('--floating-button-bg', isDark ? "#ffffff" : "#000000");
		root.style.setProperty('--floating-button-border', isDark ? "#000000" : "#ffffff");
		root.style.setProperty('--floating-button-text', isDark ? "#000000" : "#ffffff");
		
		floatingButton.className = "uncloseai-floating-button";
	}

	// Initial theme setup
	updateButtonTheme();

	// Watch for theme changes
	const observer = new MutationObserver(updateButtonTheme);
	observer.observe(document.documentElement, {
		attributes: true,
		attributeFilter: ["data-theme"],
	});

	// Watch for system theme changes
	if (window.matchMedia) {
		window
			.matchMedia("(prefers-color-scheme: dark)")
			.addEventListener("change", updateButtonTheme);
	}

	// Hover effects handled by CSS

	// Set up self-contained click handler
	setupFloatingButtonHandler(floatingButton);

	document.body.appendChild(floatingButton);
	console.log("uncloseai.js: Appended floating button to document.body.");
}

// Function to create floating AI button
export function createFloatingAIButton() {
	console.log("uncloseai.js: createFloatingAIButton() called.");

	// Load CSS for floating button if not already loaded
	const hasPicoCSS = document.querySelector('link[href*="pico"]') !== null;
	const cssFile = hasPicoCSS ? 'uncloseai-modal-pico.css' : 'uncloseai-modal-builtin.css';
	
	if (!document.querySelector(`link[href*="${cssFile}"]`)) {
		const link = document.createElement('link');
		link.rel = 'stylesheet';
		link.href = `https://uncloseai.com/src/${cssFile}`;
		
		// Create button only after CSS loads
		link.onload = () => {
			console.log(`uncloseai.js: ${cssFile} loaded, creating floating button.`);
			createAndAppendFloatingButton();
		};
		
		link.onerror = () => {
			console.error(`uncloseai.js: Failed to load ${cssFile}, creating button anyway.`);
			createAndAppendFloatingButton();
		};
		
		document.head.appendChild(link);
		return; // Exit early, button will be created after CSS loads
	}
	
	// CSS already loaded, create button immediately
	createAndAppendFloatingButton();
}

// Set up click handler for floating button (self-contained)
function setupFloatingButtonHandler(floatingButton) {
	floatingButton.onclick = async () => {
		console.log("Floating button clicked!");

		// Show loading state
		const originalText = floatingButton.textContent;
		floatingButton.textContent = "loading...";
		floatingButton.disabled = true;
		floatingButton.classList.add("loading");

		try {
			console.log("Trying to toggle Hermes modal...");

			// Try multiple ways to access the modal function
			let modalFunction = null;

			if (typeof toggleUncloseaiEmbeddedModal === "function") {
				console.log("Found toggleUncloseaiEmbeddedModal in local scope");
				modalFunction = toggleUncloseaiEmbeddedModal;
			} else if (typeof window.toggleUncloseaiEmbeddedModal === "function") {
				console.log("Found toggleUncloseaiEmbeddedModal on window");
				modalFunction = window.toggleUncloseaiEmbeddedModal;
			} else {
				console.log(
					"toggleUncloseaiEmbeddedModal not found, trying dynamic import",
				);
				// Try to import it dynamically as a fallback
				try {
					const uiModule = await import("./ui.js");
					if (uiModule.toggleUncloseaiEmbeddedModal) {
						console.log("Successfully imported toggleUncloseaiEmbeddedModal");
						modalFunction = uiModule.toggleUncloseaiEmbeddedModal;
					}
				} catch (importError) {
					console.error(
						"Could not import toggleUncloseaiEmbeddedModal:",
						importError,
					);
				}
			}

			if (modalFunction) {
				console.log("Calling modal function");
				await modalFunction();
			} else {
				// Fallback: directly open the modal
				console.log("Using fallback to directly open modal");
				await openUncloseaiEmbeddedModal();
			}
		} catch (error) {
			console.error("Error opening modal:", error);
			alert(getUIText("errorOpeningModal", { error: error.message }));
		} finally {
			// Restore button state
			floatingButton.textContent = originalText;
			floatingButton.disabled = false;
			floatingButton.classList.remove("loading");
		}
	};
}

// New mobile-first Hermes modal

// Modal functions extracted to uncloseai-embed-modal.js


export function initializeSystem() {
	// Check for skip init flag - allow partial initialization for preview windows
	if (window.UNCLOSEAI_SKIP_INIT === true) {
		console.log("uncloseai.js: Partial initialization for preview window");
		// Only create floating button in preview windows if not explicitly disabled
		if (window.UNCLOSEAI_FLOATING_BUTTON !== false) {
			console.log("uncloseai.js: Creating floating button in preview window");
			// Ensure DOM is ready before creating button
			if (document.readyState === "loading") {
				document.addEventListener("DOMContentLoaded", createFloatingAIButton);
			} else {
				createFloatingAIButton();
			}
		}
		return;
	}

	console.log("uncloseai.js: Full initialization");
	initializeChatInterface();

	// Only create floating button if not disabled
	if (SHOW_FLOATING_BUTTON) {
		console.log(
			"uncloseai.js: SHOW_FLOATING_BUTTON is true, calling createFloatingAIButton()",
		);
		createFloatingAIButton();
	} else {
		console.log(
			"uncloseai.js: SHOW_FLOATING_BUTTON is false, not creating floating button.",
		);
	}

	// Initialize class-based elements
	initializeUncloseaiElements();
}

// Toggle function for floating button
export async function toggleUncloseaiEmbeddedModal() {
	console.log("toggleUncloseaiEmbeddedModal called");
	
	// Check if modal is already open
	const existingModal = document.getElementById("uncloseai-embedded-modal");
	if (existingModal && window.uncloseaiEmbeddedModalOpen) {
		console.log("Modal is open, closing it");
		existingModal.close();
		document.body.removeChild(existingModal);
		window.uncloseaiEmbeddedModalOpen = false;
	} else {
		console.log("Modal is closed, opening it");
		await openUncloseaiEmbeddedModal();
	}
}

// Export modal functions globally for onclick handlers
window.openTTSModal = openTTSModal;
window.openTranslateModal = openTranslateModal;
window.toggleUncloseaiEmbeddedModal = toggleUncloseaiEmbeddedModal;
