'use strict';

const sqlite3 = require('sqlite3').verbose();
const path = require('path');
const fs = require('fs');

/**
 * AI Memory Database
 * Stores player-specific memories and bot directives
 * Singleton pattern - one instance per bot
 */

class AIMemoryDB {
	constructor() {
		this.db = null;
		this.botName = null;
	}

	/**
	 * Initialize database connection and create tables
	 * @param {string} dbPath - Path to sqlite database file
	 * @param {string} botName - Bot name for namespacing
	 */
	async initialize(dbPath = './storage/ai-memory.db', botName = 'default') {
		if (this.db) {
			this.botName = botName;
			return; // Already initialized
		}

		const fullPath = path.resolve(dbPath);
		const dir = path.dirname(fullPath);

		// Ensure directory exists
		if (!fs.existsSync(dir)) {
			fs.mkdirSync(dir, { recursive: true });
		}

		console.log(`AI Memory: Initializing database at ${fullPath} for bot ${botName}`);

		this.db = new sqlite3.Database(fullPath);
		this.botName = botName;

		await this.createTables();
	}

	/**
	 * Create tables if they don't exist
	 */
	createTables() {
		return new Promise((resolve, reject) => {
			this.db.serialize(() => {
				// Player memories table
				this.db.run(`
					CREATE TABLE IF NOT EXISTS player_memories (
						id INTEGER PRIMARY KEY AUTOINCREMENT,
						bot_name TEXT NOT NULL,
						player_name TEXT NOT NULL,
						memory_key TEXT NOT NULL,
						memory_value TEXT,
						updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
						UNIQUE(bot_name, player_name, memory_key)
					)
				`);

				// Bot directives table - persistent instructions per bot
				this.db.run(`
					CREATE TABLE IF NOT EXISTS bot_directives (
						id INTEGER PRIMARY KEY AUTOINCREMENT,
						bot_name TEXT NOT NULL,
						directive_key TEXT NOT NULL,
						directive_value TEXT,
						updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
						UNIQUE(bot_name, directive_key)
					)
				`);

				// General memories table - for non-player-specific info
				this.db.run(`
					CREATE TABLE IF NOT EXISTS general_memories (
						id INTEGER PRIMARY KEY AUTOINCREMENT,
						bot_name TEXT NOT NULL,
						memory_key TEXT NOT NULL,
						memory_value TEXT,
						updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
						UNIQUE(bot_name, memory_key)
					)
				`);

				// Create indexes for faster lookups
				this.db.run(`CREATE INDEX IF NOT EXISTS idx_player_memories_lookup ON player_memories(bot_name, player_name)`);
				this.db.run(`CREATE INDEX IF NOT EXISTS idx_bot_directives_lookup ON bot_directives(bot_name)`);
				this.db.run(`CREATE INDEX IF NOT EXISTS idx_general_memories_lookup ON general_memories(bot_name)`);

				resolve();
			});
		});
	}

	// ========================================
	// Player Memories (Shared across all bots)
	// ========================================

	/**
	 * Set a memory about a specific player (shared across all bots)
	 */
	async setPlayerMemory(playerName, key, value) {
		return new Promise((resolve, reject) => {
			this.db.run(`
				INSERT OR REPLACE INTO player_memories (bot_name, player_name, memory_key, memory_value, updated_at)
				VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
			`, ['global', playerName, key, value], (err) => {
				if (err) reject(err);
				else resolve();
			});
		});
	}

	/**
	 * Get a specific memory about a player (shared across all bots)
	 */
	async getPlayerMemory(playerName, key) {
		return new Promise((resolve, reject) => {
			this.db.get(`
				SELECT memory_value FROM player_memories
				WHERE bot_name = 'global' AND player_name = ? AND memory_key = ?
			`, [playerName, key], (err, row) => {
				if (err) reject(err);
				else resolve(row ? row.memory_value : null);
			});
		});
	}

	/**
	 * Get all memories about a player (shared across all bots)
	 */
	async getAllPlayerMemories(playerName) {
		return new Promise((resolve, reject) => {
			this.db.all(`
				SELECT memory_key, memory_value FROM player_memories
				WHERE bot_name = 'global' AND player_name = ?
			`, [playerName], (err, rows) => {
				if (err) reject(err);
				else {
					const memories = {};
					for (const row of rows) {
						memories[row.memory_key] = row.memory_value;
					}
					resolve(memories);
				}
			});
		});
	}

	/**
	 * Delete a specific player memory (shared across all bots)
	 */
	async deletePlayerMemory(playerName, key) {
		return new Promise((resolve, reject) => {
			this.db.run(`
				DELETE FROM player_memories
				WHERE bot_name = 'global' AND player_name = ? AND memory_key = ?
			`, [playerName, key], (err) => {
				if (err) reject(err);
				else resolve();
			});
		});
	}

	/**
	 * List all players with stored memories (shared across all bots)
	 */
	async getAllKnownPlayers() {
		return new Promise((resolve, reject) => {
			this.db.all(`
				SELECT DISTINCT player_name FROM player_memories
				WHERE bot_name = 'global'
			`, (err, rows) => {
				if (err) reject(err);
				else resolve(rows.map(r => r.player_name));
			});
		});
	}

	// ========================================
	// Bot Directives
	// ========================================

	/**
	 * Set a directive for this bot
	 */
	async setDirective(key, value) {
		return new Promise((resolve, reject) => {
			this.db.run(`
				INSERT OR REPLACE INTO bot_directives (bot_name, directive_key, directive_value, updated_at)
				VALUES (?, ?, ?, CURRENT_TIMESTAMP)
			`, [this.botName, key, value], (err) => {
				if (err) reject(err);
				else resolve();
			});
		});
	}

	/**
	 * Get a specific directive
	 */
	async getDirective(key) {
		return new Promise((resolve, reject) => {
			this.db.get(`
				SELECT directive_value FROM bot_directives
				WHERE bot_name = ? AND directive_key = ?
			`, [this.botName, key], (err, row) => {
				if (err) reject(err);
				else resolve(row ? row.directive_value : null);
			});
		});
	}

	/**
	 * Get all directives for this bot
	 */
	async getAllDirectives() {
		return new Promise((resolve, reject) => {
			this.db.all(`
				SELECT directive_key, directive_value FROM bot_directives
				WHERE bot_name = ?
			`, [this.botName], (err, rows) => {
				if (err) reject(err);
				else {
					const directives = {};
					for (const row of rows) {
						directives[row.directive_key] = row.directive_value;
					}
					resolve(directives);
				}
			});
		});
	}

	// ========================================
	// General Memories
	// ========================================

	/**
	 * Set a general memory (not player-specific)
	 */
	async setGeneralMemory(key, value) {
		return new Promise((resolve, reject) => {
			this.db.run(`
				INSERT OR REPLACE INTO general_memories (bot_name, memory_key, memory_value, updated_at)
				VALUES (?, ?, ?, CURRENT_TIMESTAMP)
			`, [this.botName, key, value], (err) => {
				if (err) reject(err);
				else resolve();
			});
		});
	}

	/**
	 * Get a general memory
	 */
	async getGeneralMemory(key) {
		return new Promise((resolve, reject) => {
			this.db.get(`
				SELECT memory_value FROM general_memories
				WHERE bot_name = ? AND memory_key = ?
			`, [this.botName, key], (err, row) => {
				if (err) reject(err);
				else resolve(row ? row.memory_value : null);
			});
		});
	}

	/**
	 * Get all general memories
	 */
	async getAllGeneralMemories() {
		return new Promise((resolve, reject) => {
			this.db.all(`
				SELECT memory_key, memory_value FROM general_memories
				WHERE bot_name = ?
			`, [this.botName], (err, rows) => {
				if (err) reject(err);
				else {
					const memories = {};
					for (const row of rows) {
						memories[row.memory_key] = row.memory_value;
					}
					resolve(memories);
				}
			});
		});
	}

	/**
	 * Get all general memories with timestamps
	 */
	async getAllGeneralMemoriesWithTimestamps() {
		return new Promise((resolve, reject) => {
			this.db.all(`
				SELECT memory_key, memory_value, updated_at FROM general_memories
				WHERE bot_name = ?
				ORDER BY updated_at DESC
			`, [this.botName], (err, rows) => {
				if (err) reject(err);
				else resolve(rows);
			});
		});
	}

	// ========================================
	// Summary Methods (for AI context)
	// ========================================
	/**
	 * Get all directives as formatted string with timestamps
	 */
	async getDirectivesSummary() {
		return new Promise((resolve, reject) => {
			this.db.all(`
				SELECT directive_key, directive_value, updated_at FROM bot_directives
				WHERE bot_name = ?
				ORDER BY updated_at DESC
			`, [this.botName], (err, rows) => {
				if (err) reject(err);
				else {
					if (rows.length === 0) {
						resolve(null);
						return;
					}

					let summary = 'Active Directives:\n';
					for (const row of rows) {
						const timeAgo = this.formatTimeAgo(row.updated_at);
						summary += `- ${row.directive_key}: ${row.directive_value} (set ${timeAgo})\n`;
					}
					resolve(summary.trim());
				}
			});
		});
	}

	/**
	 * Format timestamp as relative time string
	 */
	formatTimeAgo(timestamp) {
		if (!timestamp) return 'unknown';
		const now = new Date();
		const past = new Date(timestamp);
		const diffMs = now - past;
		const diffSecs = Math.floor(diffMs / 1000);
		const diffMins = Math.floor(diffSecs / 60);
		const diffHours = Math.floor(diffMins / 60);
		const diffDays = Math.floor(diffHours / 24);

		if (diffSecs < 60) return 'just now';
		if (diffMins < 60) return `${diffMins}m ago`;
		if (diffHours < 24) return `${diffHours}h ago`;
		if (diffDays < 7) return `${diffDays}d ago`;
		return past.toLocaleDateString();
	}

	/**
	 * Get full memory context for prompt injection
	 */
	async getMemoryContext() {
		const directives = await this.getDirectivesSummary();
		const generalMemories = await this.getAllGeneralMemoriesWithTimestamps();

		let context = '';

		if (directives) {
			context += directives + '\n\n';
		}

		if (generalMemories && generalMemories.length > 0) {
			context += 'General Memories:\n';
			for (const mem of generalMemories) {
				const timeAgo = this.formatTimeAgo(mem.updated_at);
				context += `- ${mem.memory_key}: ${mem.memory_value} (stored ${timeAgo})\n`;
			}
			context += '\n';
		}

		return context || null;
	}

	/**
	 * Get memories for specific players (for prompt injection when they're online)
	 * @param {string[]} playerNames - Array of player names to get memories for
	 */
	async getPlayerMemoriesForPrompt(playerNames) {
		if (!playerNames || playerNames.length === 0) return null;

		let context = '';
		for (const playerName of playerNames) {
			const memories = await this.getAllPlayerMemories(playerName);
			if (Object.keys(memories).length > 0) {
				context += `Memories about ${playerName}:\n`;
				for (const [key, value] of Object.entries(memories)) {
					context += `- ${key}: ${value}\n`;
				}
				context += '\n';
			}
		}
		return context.trim() || null;
	}

	// ========================================
	// Utility Methods
	// ========================================

	/**
	 * Close database connection
	 */
	async close() {
		return new Promise((resolve, reject) => {
			if (this.db) {
				this.db.close((err) => {
					if (err) reject(err);
					else {
						this.db = null;
						this.botName = null;
						resolve();
					}
				});
			} else {
				resolve();
			}
		});
	}
}

// Singleton instance
module.exports = new AIMemoryDB();
