'use strict';

// Require log-web early to capture all console output from other modules
const LogWeb = require('./log-web');

const {sleep} = require('../utils');
const conf = require('../conf');
const {CJbot} = require('../model/minecraft');

const commands = require('./commands');

CJbot.pluginAdd(require('./swing'));
CJbot.pluginAdd(require('./craft'));
CJbot.pluginAdd(require('./tp'));
CJbot.pluginAdd(require('./ai'));
CJbot.pluginAdd(require('./guardianFarm'));
CJbot.pluginAdd(require('./goldFarm'));
CJbot.pluginAdd(require('./storage'));
CJbot.pluginAdd(require('./auto-eat'));
CJbot.pluginAdd(require('./farm-supply'));
CJbot.pluginAdd(require('./commands/navigation'));

for(let name in conf.mc.bots){
	if(CJbot.bots[name]) continue;
	let bot = new CJbot({name, host: conf.mc.host, ...conf.mc.bots[name]});
	CJbot.bots[name] = bot;

	for(let command of conf.mc.bots[name].commands || ['default']){
		for(let [name, toAdd] of Object.entries(commands[command])){
			bot.addCommand(name, toAdd)
		}
	}
}

const Database = require('./storage/database');
const SettingsManager = require('./settings/manager');

// Start web server immediately — it serves static/web routes independently of bot state
const webServer = require('./web-server');
const ActivityWeb = require('./activity-web');
const ChatWeb = require('./chat-web');
const InvitePlugin = require('./invite');
webServer.queuePlugin(ChatWeb);
webServer.queuePlugin(ActivityWeb);
webServer.queuePlugin(LogWeb);
webServer.queuePlugin(InvitePlugin);
const SettingsWeb = require('./settings/web');
webServer.queuePlugin(SettingsWeb);
webServer.start().catch(err => console.error('Failed to start web server:', err));

async function initDatabase() {
	if (Database.db) return;

	await Database.initialize(conf.storage.dbPath || './storage/storage.db');
	console.log('DB initialized');

	// Seed settings defaults (INSERT OR IGNORE — won't overwrite user changes)
	const registry = SettingsManager.getRegistry();
	const seedDefaults = registry.map(r => {
		const parts = r.key.split('.');
		let node = conf;
		for (const p of parts) node = node?.[p];

		let value = '';
		if (node !== undefined && node !== null) {
			if (r.key === 'ai.prompts' && typeof node === 'object') {
				const templates = {};
				for (const [name, fn] of Object.entries(node)) {
					if (typeof fn === 'function') {
						const fnStr = fn.toString();
						const m = fnStr.match(/=>\s*`([\s\S]*)`\s*$/);
						templates[name] = m ? m[1] : '';
					}
				}
				value = JSON.stringify(templates);
			} else if (r.type === 'json') {
				value = JSON.stringify(node);
			} else {
				value = String(node);
			}
		}

		return { key: r.key, value, type: r.type, category: r.category, label: r.label, description: r.description };
	});
	await Database.seedDefaultSettings(seedDefaults);

	// Initialize settings cache from DB
	await SettingsManager.initialize();

	// Seed per-bot settings from config defaults
	await Database.seedDefaultBotSettings();

	// Apply DB-stored bot settings to live bot instances
	for (const [botName, bot] of Object.entries(CJbot.bots)) {
		try {
			const rows = await Database.getAllBotSettings(botName);
			for (const row of rows) {
				let val = row.value;
				if (row.type === 'number') val = Number(val) || 0;
				else if (row.type === 'boolean') val = val === 'true';
				else if (row.type === 'json') { try { val = JSON.parse(val); } catch (e) { val = null; } }

				switch (row.key) {
				case 'username': if (val) bot.username = val; break;
				case 'password': if (val) bot.password = val; break;
				case 'auth': if (val) bot.auth = val; break;
				case 'autoConnect': bot.autoConnect = val; break;
				case 'autoReConnect': bot.autoReConnect = val; break;
				case 'onDemand': bot.onDemand = val; break;
				case 'idleTimeout': bot._idleTimeout = Number(val) || 30000; break;
				case 'commands': bot._dbCommands = val; break;
				case 'plugins': bot.pluginsWanted = val || {}; break;
				case 'hasAi': bot.hasAi = val; break;
				}
			}
			console.log(`Applied DB settings for ${botName}`);
		} catch (e) { /* bot may not exist in DB yet */ }
	}

	if (conf.invite?.seedSites) {
		await Database.seedInviteSites(conf.invite.seedSites);
		console.log('Invite sites seeded');
	}
}

(async () => {
	try {
		// DB and settings must be ready before bots connect, so Storage
		// constructors always see the correct DB-stored config values.
		await initDatabase();

		for (let name in CJbot.bots) {
			const bot = CJbot.bots[name];
			if (bot.autoConnect) {
				console.log('Trying to connect', name);
				console.log('Status for', name, await bot.connect());
				await sleep(30000);
			}
		}
	} catch (e) {
		console.log('!!!!!!!! error:', e);
	}
})();
