import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
import { Database } from 'bun:sqlite';
import { initializeDatabase, ensureUserExists } from '../../../src/db';
import { TaskService } from '../../../src/tasks/service';

let memDb: Database;

beforeEach(() => {
  memDb = new Database(':memory:');
  initializeDatabase(memDb);
  TaskService.dbInstance = memDb;
});

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

describe('TaskService.listGroupUnassigned / countGroupUnassigned', () => {
  it('devuelve solo tareas sin dueño del grupo, en orden por fecha (NULL al final) y respeta el límite', () => {
    // Sembrar grupos para FK
    memDb.prepare(`INSERT OR IGNORE INTO groups (id, community_id, name, active) VALUES (?, ?, ?, 1)`)
      .run('g1@g.us', 'c1', 'G1');
    memDb.prepare(`INSERT OR IGNORE INTO groups (id, community_id, name, active) VALUES (?, ?, ?, 1)`)
      .run('g2@g.us', 'c1', 'G2');

    // En g1: 3 sin dueño con distintas fechas, 1 asignada (excluir), 1 sin dueño completada (excluir)
    const tEarly = TaskService.createTask({
      description: 'Sin dueño pronto',
      due_date: '2025-08-01',
      group_id: 'g1@g.us',
      created_by: '1000000000',
    });
    const tLater = TaskService.createTask({
      description: 'Sin dueño tarde',
      due_date: '2025-10-01',
      group_id: 'g1@g.us',
      created_by: '1000000000',
    });
    const tNull = TaskService.createTask({
      description: 'Sin dueño sin fecha',
      due_date: null,
      group_id: 'g1@g.us',
      created_by: '1000000000',
    });
    TaskService.createTask({
      description: 'Asignada (no debe salir)',
      due_date: '2025-09-01',
      group_id: 'g1@g.us',
      created_by: '1000000000',
    }, [{ user_id: '2000000000', assigned_by: '1000000000' }]);

    // Completar una sin dueño para que no aparezca
    memDb.prepare(`UPDATE tasks SET completed = 1, completed_at = CURRENT_TIMESTAMP WHERE id = ?`).run(tEarly);

    // En g2: sin dueño (no deben aparecer al listar g1)
    for (let i = 1; i <= 3; i++) {
      TaskService.createTask({
        description: `G2 ${i}`,
        due_date: '2025-12-31',
        group_id: 'g2@g.us',
        created_by: '1000000000',
      });
    }

    const list = TaskService.listGroupUnassigned('g1@g.us', 10);
    expect(Array.isArray(list)).toBe(true);
    // Deben quedar 2: 'Sin dueño tarde' (fecha) y 'Sin dueño sin fecha' (NULL al final)
    expect(list.length).toBe(2);
    expect(list[0].description).toBe('Sin dueño tarde');
    expect(list[1].description).toBe('Sin dueño sin fecha');
    expect(list[0].assignees).toEqual([]);
    expect(list[1].assignees).toEqual([]);
    expect(list[0].group_id).toBe('g1@g.us');
    expect(list[1].group_id).toBe('g1@g.us');

    // Límite
    // Crear más en g1 para forzar límite 1
    TaskService.createTask({
      description: 'Otra sin dueño',
      due_date: '2025-11-01',
      group_id: 'g1@g.us',
      created_by: '1000000000',
    });
    const limited = TaskService.listGroupUnassigned('g1@g.us', 1);
    expect(limited.length).toBe(1);
  });

  it('countGroupUnassigned cuenta solo sin dueño pendientes (excluye completadas y asignadas)', () => {
    // Limpiar por si acaso
    memDb.exec('DELETE FROM tasks');
    memDb.exec('DELETE FROM task_assignments');

    // Sembrar grupo
    memDb.prepare(`INSERT OR IGNORE INTO groups (id, community_id, name, active) VALUES (?, ?, ?, 1)`)
      .run('g1@g.us', 'c1', 'G1');

    const a = TaskService.createTask({
      description: 'Sin dueño 1',
      due_date: '2025-09-10',
      group_id: 'g1@g.us',
      created_by: '3333333333',
    });
    const b = TaskService.createTask({
      description: 'Sin dueño 2',
      due_date: null,
      group_id: 'g1@g.us',
      created_by: '3333333333',
    });
    // Asignada (no cuenta como sin dueño)
    TaskService.createTask({
      description: 'Asignada',
      due_date: '2025-09-12',
      group_id: 'g1@g.us',
      created_by: '3333333333',
    }, [{ user_id: '4444444444', assigned_by: '3333333333' }]);

    // Completar una sin dueño
    memDb.prepare(`UPDATE tasks SET completed = 1, completed_at = CURRENT_TIMESTAMP WHERE id = ?`).run(a);

    const cnt = TaskService.countGroupUnassigned('g1@g.us');
    expect(cnt).toBe(1); // sólo queda b
  });
});

describe('TaskService.createTask', () => {
  it('crea una tarea mínima con created_by y sin due_date ni group_id', () => {
    const creatorRaw = '555111222@s.whatsapp.net';
    const createdBy = ensureUserExists(creatorRaw, memDb);
    expect(createdBy).toBeTruthy();

    const id = TaskService.createTask(
      {
        description: 'Comprar agua',
        created_by: createdBy!,
        due_date: null,
        group_id: null,
      },
      [] // sin asignaciones
    );

    expect(typeof id).toBe('number');

    const task = memDb
      .prepare(
        `SELECT id, description, due_date, group_id, created_by, completed 
         FROM tasks WHERE id = ?`
      )
      .get(id) as any;

    expect(task).toBeTruthy();
    expect(task.description).toBe('Comprar agua');
    expect(task.due_date).toBeNull();
    expect(task.group_id).toBeNull();
    expect(task.created_by).toBe(createdBy);
    expect(task.completed).toBe(0); // BOOLEAN en SQLite como 0/1
  });

  it('guarda due_date como cadena YYYY-MM-DD y group_id como JID completo', () => {
    const creatorRaw = '555333444@s.whatsapp.net';
    const createdBy = ensureUserExists(creatorRaw, memDb)!;

    const due = '2025-09-10';
    const groupId = '12345@g.us';

    // Sembrar el grupo para satisfacer la FK de tasks.group_id -> groups.id
    memDb
      .prepare(`INSERT INTO groups (id, community_id, name, active) VALUES (?, ?, ?, TRUE)`)
      .run(groupId, 'test-community', 'Grupo de prueba');

    const id = TaskService.createTask(
      {
        description: 'Pagar servicios',
        created_by: createdBy,
        due_date: due,
        group_id: groupId,
      },
      []
    );

    const row = memDb
      .prepare(
        `SELECT description, due_date, group_id, created_by 
         FROM tasks WHERE id = ?`
      )
      .get(id) as any;

    expect(row.due_date).toBe(due);
    expect(row.group_id).toBe(groupId);
    expect(row.created_by).toBe(createdBy);
  });

  it('inserta asignaciones y evita duplicados por usuario', () => {
    const creator = ensureUserExists('555000001@s.whatsapp.net', memDb)!;
    const assigneeA = ensureUserExists('555000002@s.whatsapp.net', memDb)!;
    const assigneeB = ensureUserExists('555000003@s.whatsapp.net', memDb)!;

    const id = TaskService.createTask(
      {
        description: 'Organizar reunión',
        created_by: creator,
        due_date: null,
        group_id: null,
      },
      [
        { user_id: assigneeA, assigned_by: creator },
        { user_id: assigneeA, assigned_by: creator }, // duplicado
        { user_id: assigneeB, assigned_by: creator },
      ]
    );

    const count = memDb
      .prepare(
        `SELECT COUNT(*) AS c FROM task_assignments WHERE task_id = ?`
      )
      .get(id) as any;

    expect(count.c).toBe(2);

    const users = memDb
      .prepare(
        `SELECT user_id FROM task_assignments WHERE task_id = ? ORDER BY user_id`
      )
      .all(id) as any[];

    expect(users.map(u => u.user_id)).toEqual([assigneeA, assigneeB].sort());
  });

  it('asegura usuarios inexistentes en asignaciones y no viola FK', () => {
    const creator = ensureUserExists('555010101@s.whatsapp.net', memDb)!;

    const id = TaskService.createTask(
      {
        description: 'Tarea con asignado nuevo',
        created_by: creator,
        due_date: null,
        group_id: null,
      },
      [
        // Usuario no existente: TaskService debe asegurar y crear
        { user_id: '555099999', assigned_by: creator },
      ]
    );

    const counts = memDb
      .prepare(
        `SELECT 
           (SELECT COUNT(*) FROM tasks) AS tasks_count,
           (SELECT COUNT(*) FROM task_assignments WHERE task_id = ?) AS assigns_count`
      )
      .get(id) as any;

    expect(counts.tasks_count).toBe(1);
    expect(counts.assigns_count).toBe(1);
  });

  it('lanza error si created_by es inválido (no normalizable) y no inserta la tarea', () => {
    const invalidCreator = 'invalid-id!';

    expect(() =>
      TaskService.createTask(
        {
          description: 'No debería insertarse',
          created_by: invalidCreator,
          due_date: null,
          group_id: null,
        },
        []
      )
    ).toThrow();

    const count = memDb
      .prepare(`SELECT COUNT(*) AS c FROM tasks`)
      .get() as any;

    expect(count.c).toBe(0);
  });
});
