"use strict";

// Módulo de base de datos para WebExtensions
// Usa sql.js (SQLite compilado a JavaScript/WebAssembly)

const DB = {
	db: null,
	SQL: null,

	/**
	 * Inicializa sql.js (debe ser llamado primero)
	 */
	async initSQL() {
		if (this.SQL) return this.SQL;
		
		// Cargar sql.js desde archivos locales de la extensión
		try {
			const browserAPI = typeof browser !== "undefined" ? browser : chrome;
			
			// Intentar cargar sql.js si no está ya cargado
			if (typeof window.initSqlJs === "undefined") {
				const sqlJsUrl = browserAPI.runtime.getURL("content/sql-wasm.js");
				await this.loadScript(sqlJsUrl);
			}
			
			this.SQL = await initSqlJs({
				locateFile: file => browserAPI.runtime.getURL(`content/${file}`)
			});
			
			return this.SQL;
		} catch (e) {
			console.error("Failed to initialize sql.js:", e);
			throw new Error("Could not load SQL engine");
		}
	},

	/**
	 * Cargar script dinámicamente
	 */
	loadScript(src) {
		return new Promise((resolve, reject) => {
			const script = document.createElement("script");
			script.src = src;
			script.onload = resolve;
			script.onerror = reject;
			document.head.appendChild(script);
		});
	},

	/**
	 * Abre o crea la base de datos
	 * @param {string} dbname - Nombre del archivo de base de datos
	 * @param {Document} document - Documento HTML
	 * @returns {boolean} - true si se abrió correctamente
	 */
	async openDB(dbname, document) {
		try {
			await this.initSQL();

			// Intentar cargar la base de datos desde el storage de la extensión
			const dbData = await this.loadDBFromStorage(dbname);
			
			if (dbData) {
				// Cargar base de datos existente
				this.db = new this.SQL.Database(new Uint8Array(dbData));
				console.log("Database loaded from storage");
			} else {
				// Intentar descargar la base de datos empaquetada
				await this.downloadAndStoreDB(dbname, document);
			}

			return true;
		} catch (e) {
			console.error("Error opening database:", e);
			this.showMessage(document, 
				"Error loading database. Please check the console for details.", 
				"db-warning"
			);
			return false;
		}
	},

	/**
	 * Carga la base de datos desde el storage local de la extensión
	 */
	async loadDBFromStorage(dbname) {
		try {
			const browserAPI = typeof browser !== "undefined" ? browser : chrome;
			const result = await browserAPI.storage.local.get(dbname);
			return result[dbname];
		} catch (e) {
			console.warn("Could not load DB from storage:", e);
			return null;
		}
	},

	/**
	 * Descarga y almacena la base de datos
	 */
	async downloadAndStoreDB(dbname, document) {
		this.showMessage(document, 
			"Loading database for the first time. Please wait...", 
			"db-warning ok"
		);

		try {
			// Intentar cargar desde la extensión
			const browserAPI = typeof browser !== "undefined" ? browser : chrome;
			const dbUrl = browserAPI.runtime.getURL(`content/db/${dbname}`);
			
			const response = await fetch(dbUrl);
			if (!response.ok) {
				throw new Error(`Failed to fetch database: ${response.status}`);
			}

			const arrayBuffer = await response.arrayBuffer();
			this.db = new this.SQL.Database(new Uint8Array(arrayBuffer));

			// Guardar en storage para uso futuro
			await this.saveDBToStorage(dbname, arrayBuffer);

			this.showMessage(document, 
				"Database loaded successfully!", 
				"db-warning ok"
			);

			console.log("Database downloaded and stored");
		} catch (e) {
			console.error("Error downloading database:", e);
			this.showMessage(document, 
				"Failed to load database. Please reinstall the extension.", 
				"db-warning bad"
			);
			throw e;
		}
	},

	/**
	 * Guarda la base de datos en el storage local
	 */
	async saveDBToStorage(dbname, arrayBuffer) {
		try {
			const browserAPI = typeof browser !== "undefined" ? browser : chrome;
			
			// Convertir ArrayBuffer a Array para storage
			const uint8Array = new Uint8Array(arrayBuffer);
			const array = Array.from(uint8Array);
			
			// Chrome tiene límite de storage, considerar usar chunks para DBs grandes
			// Por ahora, intentar guardar directamente
			await browserAPI.storage.local.set({ [dbname]: array });
			
			console.log("Database saved to storage");
		} catch (e) {
			console.warn("Could not save DB to storage (may be too large):", e);
			// No es crítico si falla, se recargará la próxima vez
		}
	},

	/**
	 * Muestra un mensaje en la página
	 */
	showMessage(document, msg, style) {
		const div = document.createElement("div");
		div.className = style;
		div.appendChild(document.createTextNode(msg));
		const page = document.getElementById("page");
		if (page) {
			// Limpiar solo contenido dinámico, mantener cabecera
			Array.from(page.children).forEach(child => {
				if (!child.classList.contains('amo-header')) child.remove();
			});
			page.appendChild(div);
		}
	},

	/**
	 * Cierra la base de datos
	 */
	closeDB() {
		if (this.db) {
			this.db.close();
			this.db = null;
		}
	},

	/**
	 * Ejecuta una consulta SQL
	 */
	execute(sql, params = []) {
		if (!this.db) {
			throw new Error("Database not opened");
		}
		return this.db.exec(sql, params);
	},

	/**
	 * Ejecuta una consulta y retorna la primera fila
	 */
	executeRow(sql, params = []) {
		const results = this.execute(sql, params);
		if (results.length > 0 && results[0].values.length > 0) {
			return results[0].values[0];
		}
		return null;
	},

	/**
	 * Prepara una statement (compatible con API antigua)
	 */
	createStatement(sql) {
		if (!this.db) {
			throw new Error("Database not opened");
		}
		
		// Crear un objeto que emule la API de mozIStorageStatement
		const stmt = this.db.prepare(sql);
		let currentRow = null;
		const columnNames = stmt.getColumnNames();
		const paramsObj = {};
		let paramsBound = false;
		
		return {
			params: paramsObj,
			executeStep() {
				// Bind parameters only once before first execution
				if (!paramsBound && Object.keys(paramsObj).length > 0) {
					// Convert params object to sql.js format (add ':' prefix to keys)
					const boundParams = {};
					for (const key in paramsObj) {
						boundParams[':' + key] = paramsObj[key];
					}
					stmt.bind(boundParams);
					paramsBound = true;
				}
				
				const hasRow = stmt.step();
				if (hasRow) {
					const values = stmt.get();
					currentRow = {};
					columnNames.forEach((name, index) => {
						currentRow[name] = values[index];
					});
				}
				return hasRow;
			},
			reset() {
				stmt.reset();
				currentRow = null;
				paramsBound = false;
			},
			finalize() {
				stmt.free();
				currentRow = null;
			},
			get row() {
				return currentRow || {};
			},
			getString(index) {
				return currentRow ? Object.values(currentRow)[index] : null;
			},
			getInt32(index) {
				return currentRow ? Object.values(currentRow)[index] : 0;
			},
			getDouble(index) {
				return currentRow ? Object.values(currentRow)[index] : 0.0;
			}
		};
	}
};

// Hacer disponible globalmente para compatibilidad
if (typeof window !== "undefined") {
	window.DB = DB;
}
