import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
import { Database } from 'bun:sqlite';
import { initializeDatabase } from '../../../src/db';
import { CommandService } from '../../../src/services/command';
import { TaskService } from '../../../src/tasks/service';
import { GroupSyncService } from '../../../src/services/group-sync';
import { setDb, resetDb } from '../../../src/db/locator';

let memDb: Database;
const testContextBase = {
	sender: '1234567890',
	groupId: 'test-group@g.us',
	mentions: [] as string[],
};

beforeEach(() => {
	memDb = new Database(':memory:');
	initializeDatabase(memDb);
	setDb(memDb);
	GroupSyncService.activeGroupsCache.clear();
});

test('listar grupo por defecto con t ver en grupo e incluir “… y X más”', async () => {
	// Insert group and cache it as active
	memDb.exec(`
    INSERT OR IGNORE INTO groups (id, community_id, name, active)
    VALUES ('test-group@g.us', 'test-community', 'Test Group', 1)
  `);
	GroupSyncService.activeGroupsCache.clear();
	GroupSyncService.activeGroupsCache.set('test-group@g.us', 'Test Group');

	// Crear 12 tareas sin asignados en el grupo
	for (let i = 1; i <= 12; i++) {
		TaskService.createTask({
			description: `Task ${i}`,
			due_date: '2025-12-31',
			group_id: 'test-group@g.us',
			created_by: '1234567890',
		});
	}

	const responses = await CommandService.handle({
		sender: '1234567890',
		groupId: 'test-group@g.us',
		mentions: [],
		message: 't ver'
	});

	expect(responses.length).toBe(1);
	expect(responses[0].recipient).toBe('1234567890');
	expect(responses[0].message).toContain('No respondo en grupos.');
});

test('listar “mis” por defecto en DM con t ver', async () => {
	// Insert groups and cache them
	memDb.exec(`
    INSERT OR REPLACE INTO groups (id, community_id, name, active) VALUES
      ('test-group@g.us', 'test-community', 'Test Group', 1),
      ('group-2@g.us', 'test-community', 'Group 2', 1)
  `);
	GroupSyncService.activeGroupsCache.clear();
	GroupSyncService.activeGroupsCache.set('test-group@g.us', 'Test Group');
	GroupSyncService.activeGroupsCache.set('group-2@g.us', 'Group 2');

	// Crear 2 tareas asignadas al usuario en distintos grupos
	const t1 = TaskService.createTask({
		description: 'G1 Task',
		due_date: '2025-11-20',
		group_id: 'test-group@g.us',
		created_by: '1111111111',
	}, [{ user_id: '1234567890', assigned_by: '1111111111' }]);

	const t2 = TaskService.createTask({
		description: 'G2 Task',
		due_date: '2025-11-25',
		group_id: 'group-2@g.us',
		created_by: '2222222222',
	}, [{ user_id: '1234567890', assigned_by: '2222222222' }]);

	const responses = await CommandService.handle({
		sender: '1234567890',
		// Contexto de DM: usar un JID que NO sea de grupo
		groupId: '1234567890@s.whatsapp.net',
		mentions: [],
		message: 't ver'
	});

	expect(responses.length).toBe(1);
	expect(responses[0].recipient).toBe('1234567890');
	const msg = responses[0].message;
	expect(msg).toContain('Test Group');
	expect(msg).toContain('Group 2');
	expect(msg).toMatch(/- `\d{4}` G1 Task/);
	expect(msg).toMatch(/- `\d{4}` G2 Task/);
});

test('completar tarea: camino feliz, ya completada y no encontrada', async () => {
	// Insertar grupo y cache
	memDb.exec(`
    INSERT OR IGNORE INTO groups (id, community_id, name, active)
    VALUES ('test-group@g.us', 'test-community', 'Test Group', 1)
  `);
	GroupSyncService.activeGroupsCache.clear();
	GroupSyncService.activeGroupsCache.set('test-group@g.us', 'Test Group');

	// Hacer a 1234567890 miembro activo del grupo para permisos de completado
	memDb.exec(`
    INSERT OR IGNORE INTO users (id) VALUES ('1234567890');
    INSERT OR REPLACE INTO group_members (group_id, user_id, is_active) VALUES ('test-group@g.us', '1234567890', 1);
  `);

	const taskId = TaskService.createTask({
		description: 'Completar yo',
		due_date: '2025-10-10',
		group_id: 'test-group@g.us',
		created_by: '1111111111',
	});

	const dc = Number((memDb.prepare(`SELECT display_code FROM tasks WHERE id = ?`).get(taskId) as any)?.display_code || 0);

	// 1) Camino feliz
	let responses = await CommandService.handle({
		sender: '1234567890',
		groupId: 'test-group@g.us',
		mentions: [],
		message: `t x ${dc}`
	});
	expect(responses.length).toBe(1);
	expect(responses[0].recipient).toBe('1234567890');
	expect(responses[0].message).toMatch(/^✅ `\d{4}` _completada_/);

	// 2) Ya completada (ahora no debe resolverse por display_code → no encontrada)
	responses = await CommandService.handle({
		sender: '1234567890',
		groupId: 'test-group@g.us',
		mentions: [],
		message: `t x ${dc}`
	});
	expect(responses.length).toBe(1);
	expect(responses[0].message).toContain('no encontrada');

	// 3) No encontrada
	responses = await CommandService.handle({
		sender: '1234567890',
		groupId: 'test-group@g.us',
		mentions: [],
		message: `t x 999999`
	});
	expect(responses.length).toBe(1);
	expect(responses[0].message).toContain('no encontrada');
});

test('ver sin en grupo activo: solo sin dueño y paginación', async () => {
	// Insertar grupo y cachearlo como activo
	memDb.exec(`
    INSERT OR IGNORE INTO groups (id, community_id, name, active)
    VALUES ('test-group@g.us', 'test-community', 'Test Group', 1)
  `);
	GroupSyncService.activeGroupsCache.clear();
	GroupSyncService.activeGroupsCache.set('test-group@g.us', 'Test Group');

	// 12 tareas sin dueño (para provocar “… y 2 más” con límite 10)
	for (let i = 1; i <= 12; i++) {
		TaskService.createTask({
			description: `Unassigned ${i}`,
			due_date: '2025-12-31',
			group_id: 'test-group@g.us',
			created_by: '9999999999',
		});
	}

	// 2 tareas asignadas (no deben aparecer en "ver sin")
	TaskService.createTask({
		description: 'Asignada 1',
		due_date: '2025-11-01',
		group_id: 'test-group@g.us',
		created_by: '1111111111',
	}, [{ user_id: '1234567890', assigned_by: '1111111111' }]);

	TaskService.createTask({
		description: 'Asignada 2',
		due_date: '2025-11-02',
		group_id: 'test-group@g.us',
		created_by: '1111111111',
	}, [{ user_id: '1234567890', assigned_by: '1111111111' }]);

	const responses = await CommandService.handle({
		sender: '1234567890',
		groupId: 'test-group@g.us',
		mentions: [],
		message: 't ver sin'
	});

	expect(responses.length).toBe(1);
	expect(responses[0].recipient).toBe('1234567890');
	const msg = responses[0].message;
	expect(msg).toContain('No respondo en grupos.');
});

test('ver sin por DM devuelve instrucción', async () => {
	const responses = await CommandService.handle({
		sender: '1234567890',
		// DM: no es un JID de grupo
		groupId: '1234567890@s.whatsapp.net',
		mentions: [],
		message: 't ver sin'
	});

	expect(responses.length).toBe(1);
	expect(responses[0].recipient).toBe('1234567890');
	expect(responses[0].message).toContain('No tienes tareas pendientes.');
});

test('ver todos en grupo: “Tus tareas” + “Sin dueño (grupo actual)” con paginación en la sección sin dueño', async () => {
	// Insertar grupo y cachearlo como activo
	memDb.exec(`
    INSERT OR IGNORE INTO groups (id, community_id, name, active)
    VALUES ('test-group@g.us', 'test-community', 'Test Group', 1)
  `);
	GroupSyncService.activeGroupsCache.clear();
	GroupSyncService.activeGroupsCache.set('test-group@g.us', 'Test Group');

	// Tus tareas (2 asignadas al usuario)
	TaskService.createTask({
		description: 'Mi Tarea 1',
		due_date: '2025-10-10',
		group_id: 'test-group@g.us',
		created_by: '2222222222',
	}, [{ user_id: '1234567890', assigned_by: '2222222222' }]);

	TaskService.createTask({
		description: 'Mi Tarea 2',
		due_date: '2025-10-11',
		group_id: 'test-group@g.us',
		created_by: '2222222222',
	}, [{ user_id: '1234567890', assigned_by: '2222222222' }]);

	// 12 sin dueño en el grupo (provoca “… y 2 más” en esa sección)
	for (let i = 1; i <= 12; i++) {
		TaskService.createTask({
			description: `Sin dueño ${i}`,
			due_date: '2025-12-31',
			group_id: 'test-group@g.us',
			created_by: '9999999999',
		});
	}

	const responses = await CommandService.handle({
		sender: '1234567890',
		groupId: 'test-group@g.us',
		mentions: [],
		message: 't ver todos'
	});

	expect(responses.length).toBe(1);
	const msg = responses[0].message;
	expect(msg).toContain('No respondo en grupos.');
});

test('ver todos por DM: “Tus tareas” + nota instructiva para ver sin dueño desde el grupo', async () => {
	// 2 tareas asignadas al usuario en cualquier grupo (no importa para este test)
	TaskService.createTask({
		description: 'Mi Tarea A',
		due_date: '2025-11-20',
		group_id: 'group-1@g.us',
		created_by: '1111111111',
	}, [{ user_id: '1234567890', assigned_by: '1111111111' }]);

	TaskService.createTask({
		description: 'Mi Tarea B',
		due_date: '2025-11-21',
		group_id: 'group-2@g.us',
		created_by: '1111111111',
	}, [{ user_id: '1234567890', assigned_by: '1111111111' }]);

	const responses = await CommandService.handle({
		sender: '1234567890',
		groupId: '1234567890@s.whatsapp.net', // DM
		mentions: [],
		message: 't ver todos'
	});

	expect(responses.length).toBe(1);
	const msg = responses[0].message;
	expect(msg).toContain('Tus tareas');
	expect(msg).toContain('ℹ️ Para ver tareas sin responsable');
});

afterEach(() => {
	try { resetDb(); memDb.close(); } catch { }
});

describe('CommandService', () => {
	test('should ignore non-tarea commands', async () => {
		const responses = await CommandService.handle({
			...testContextBase,
			message: '/othercommand'
		});
		expect(responses).toEqual([]);
	});

	test('acepta alias t y responde con formato compacto', async () => {
		const responses = await CommandService.handle({
			...testContextBase,
			message: 't n Test task'
		});

		expect(responses.length).toBe(1);
		expect(responses[0].recipient).toBe('1234567890');
		// Debe empezar con "📝 `0001` "
		expect(responses[0].message).toMatch(/^📝 `\d{4}` /);
		// Debe mostrar la descripción en texto plano (sin cursiva)
		expect(responses[0].message).toContain(' Test task');
		expect(responses[0].message).not.toContain('_Test task_');
		// No debe usar el texto antiguo "Tarea <id> creada"
		expect(responses[0].message).not.toMatch(/Tarea \d+ creada/);
	});

	test('should return error response on failure', async () => {
		// Forzar error temporalmente
		const original = TaskService.createTask;
		(TaskService as any).createTask = () => { throw new Error('forced'); };

		const responses = await CommandService.handle({
			...testContextBase,
			message: 'tarea nueva Test task'
		});

		expect(responses.length).toBe(1);
		expect(responses[0].recipient).toBe('1234567890');
		expect(responses[0].message).toBe('Error processing command');

		// Restaurar
		(TaskService as any).createTask = original;
	});
});
