//// 
import type { Database } from 'bun:sqlite';
import { CommandService } from './services/command';
import { GroupSyncService } from './services/group-sync';
import { ResponseQueue } from './services/response-queue';
import { TaskService } from './tasks/service';
import { WebhookManager } from './services/webhook-manager';
import { normalizeWhatsAppId } from './utils/whatsapp';
import { ensureUserExists, db, initializeDatabase } from './db';
// Bun is available globally when running under Bun runtime
declare global {
	var Bun: typeof import('bun');
}
export const REQUIRED_ENV = [
	'EVOLUTION_API_URL',
	'EVOLUTION_API_KEY', 
	'EVOLUTION_API_INSTANCE',
	'CHATBOT_PHONE_NUMBER',
	'WEBHOOK_URL'
];
type WebhookPayload = {
	event: string;
	instance: string;
	data: any;
	// Other fields from Evolution API
};
export class WebhookServer {
	static dbInstance: Database = db;
	private static getBaseUrl(request: Request): string {
		const proto = request.headers.get('x-forwarded-proto') || 'http';
		const host = request.headers.get('x-forwarded-host') || request.headers.get('host');
		return `${proto}://${host}`;
	}
	static async handleRequest(request: Request): Promise {
		// Health check endpoint
		const url = new URL(request.url);
		if (url.pathname.endsWith('/health')) {
			return new Response('OK', { status: 200 });
		}
		if (process.env.NODE_ENV !== 'test') {
			console.log('ℹ️ Incoming webhook request:')
		}
		// 1. Method validation
		if (request.method !== 'POST') {
			return new Response('🚫 Method not allowed', { status: 405 });
		}
		// 2. Content-Type validation
		const contentType = request.headers.get('content-type');
		if (!contentType?.includes('application/json')) {
			return new Response('🚫 Invalid content type', { status: 400 });
		}
		try {
			// 3. Parse and validate payload
			const payload = await request.json() as WebhookPayload;
			if (!payload.event || !payload.instance) {
				return new Response('🚫 Invalid payload', { status: 400 });
			}
			// 4. Verify instance matches (skip in test environment unless TEST_VERIFY_INSTANCE is set)
			if ((process.env.NODE_ENV !== 'test' || process.env.TEST_VERIFY_INSTANCE) &&
				payload.instance !== process.env.EVOLUTION_API_INSTANCE) {
				return new Response('🚫 Invalid instance', { status: 403 });
			}
			// 5. Route events
			// console.log('ℹ️ Webhook event received:', {
			// 	event: payload.event,
			// 	instance: payload.instance,
			// 	data: payload.data ? '[...]' : null
			// });
			// Normalize event name to handle different casing/format (e.g., MESSAGES_UPSERT)
			const evt = String(payload.event);
			const evtNorm = evt.toLowerCase().replace(/_/g, '.');
			switch (evtNorm) {
				case 'messages.upsert':
					if (process.env.NODE_ENV !== 'test') {
						console.log('ℹ️ Handling message upsert:', {
							groupId: payload.data?.key?.remoteJid,
							message: payload.data?.message?.conversation,
							rawEvent: evt
						});
					}
					await WebhookServer.handleMessageUpsert(payload.data);
					break;
				// Other events will be added later
			}
			return new Response('OK', { status: 200 });
		} catch (error) {
			console.error('❌ Error processing webhook:', {
				error: error instanceof Error ? error.message : String(error),
				stack: error instanceof Error ? error.stack : undefined,
				time: new Date().toISOString()
			});
			return new Response('Invalid request', { status: 400 });
		}
	}
	static async handleMessageUpsert(data: any) {
		if (!data?.key?.remoteJid || !data.message || !data.message.conversation) {
			if (process.env.NODE_ENV !== 'test') {
				console.log('⚠️ Invalid message format - missing required fields');
				console.log(data);
			}
			return;
		}
		// Normalize sender ID for consistency and validation
		const normalizedSenderId = normalizeWhatsAppId(data.key.participant);
		if (!normalizedSenderId) {
			if (process.env.NODE_ENV !== 'test') {
				console.log('⚠️ Invalid sender ID, ignoring message');
			}
			return;
		}
		// Ensure user exists in database
		const userId = ensureUserExists(data.key.participant, WebhookServer.dbInstance);
		if (!userId) {
			if (process.env.NODE_ENV !== 'test') {
				console.log('⚠️ Failed to ensure user exists, ignoring message');
			}
			return;
		}
		// Check if the group is active
		if (!GroupSyncService.isGroupActive(data.key.remoteJid)) {
			if (process.env.NODE_ENV !== 'test') {
				console.log('⚠️ Group is not active, ignoring message');
			}
			return;
		}
		// Forward to command service only if:
		// 1. It's a text message (has conversation field)
		// 2. Starts with /tarea command
		const messageText = data.message.conversation;
		if (typeof messageText === 'string' && messageText.trim().startsWith('/tarea')) {
			// Extraer menciones desde el mensaje
			const mentions = data.message?.contextInfo?.mentionedJid || [];
			// Asegurar que CommandService y TaskService usen la misma DB (tests/producción)
			(CommandService as any).dbInstance = WebhookServer.dbInstance;
			(TaskService as any).dbInstance = WebhookServer.dbInstance;
			// Delegar el manejo del comando
			const responses = await CommandService.handle({
				sender: normalizedSenderId,
				groupId: data.key.remoteJid,
				message: messageText,
				mentions
			});
			// Encolar respuestas si las hay
			if (responses.length > 0) {
				await ResponseQueue.add(responses);
			}
		}
	}
	static validateEnv() {
		console.log('ℹ️ Checking environment variables...');
		console.log('EVOLUTION_API_URL:', process.env.EVOLUTION_API_URL ? '***' : 'MISSING');
		console.log('EVOLUTION_API_INSTANCE:', process.env.EVOLUTION_API_INSTANCE || 'MISSING');
		console.log('WEBHOOK_URL:', process.env.WEBHOOK_URL ? `${process.env.WEBHOOK_URL.substring(0, 20)}...` : 'NOT SET');
		console.log('WHATSAPP_COMMUNITY_ID:', process.env.WHATSAPP_COMMUNITY_ID ? '***' : 'NOT SET (se mostrarán comunidades disponibles)');
		const missing = REQUIRED_ENV.filter(v => !process.env[v]);
		if (missing.length) {
			console.error('❌ Missing required environment variables:');
			missing.forEach(v => console.error(`- ${v}`));
			console.error('Add these to your CapRover environment configuration');
			process.exit(1);
		}
		if (process.env.CHATBOT_PHONE_NUMBER &&
			!/^\d+$/.test(process.env.CHATBOT_PHONE_NUMBER)) {
			console.error('❌ CHATBOT_PHONE_NUMBER must contain only digits');
			process.exit(1);
		}
	}
	static async start() {
		this.validateEnv();
		// Ensure database schema and migrations are applied
		initializeDatabase(this.dbInstance);
		const PORT = process.env.PORT || '3007';
		console.log('✅ Environment variables validated');
		if (process.env.NODE_ENV !== 'test') {
			try {
				await WebhookManager.registerWebhook();
				// Add small delay to allow webhook to propagate
				await new Promise(resolve => setTimeout(resolve, 1000));
				const isActive = await WebhookManager.verifyWebhook();
				if (!isActive) {
					console.error('❌ Webhook verification failed - retrying in 2 seconds...');
					await new Promise(resolve => setTimeout(resolve, 2000));
					const isActiveRetry = await WebhookManager.verifyWebhook();
					if (!isActiveRetry) {
						console.error('❌ Webhook verification failed after retry');
						process.exit(1);
					}
				}
				// Initialize groups - critical for operation
				await GroupSyncService.checkInitialGroups();
				// Start response queue worker (background)
				try {
					await ResponseQueue.process();
					console.log('✅ ResponseQueue worker started');
				} catch (e) {
					console.error('❌ Failed to start ResponseQueue worker:', e);
				}
			} catch (error) {
				console.error('❌ Failed to setup webhook:', error instanceof Error ? error.message : error);
				process.exit(1);
			}
		}
		const server = Bun.serve({
			port: parseInt(PORT),
			fetch: (request) => WebhookServer.handleRequest(request)
		});
		console.log(`Server running on port ${PORT}`);
		return server;
	}
}