import { describe, it, beforeEach, expect } from 'bun:test';
import { Database } from 'bun:sqlite';
import { initializeDatabase } from '../../../src/db';
import { TaskService } from '../../../src/tasks/service';
import { RemindersService } from '../../../src/services/reminders';
import { ResponseQueue } from '../../../src/services/response-queue';
import { setDb } from '../../../src/db/locator';

import { toIsoSql as toIso } from '../../helpers/dates';

describe('RemindersService', () => {
  let memdb: Database;
  const USER = '34600123456';

  beforeEach(() => {
    process.env.NODE_ENV = 'test';
    process.env.TZ = 'Europe/Madrid';
    process.env.REMINDERS_GRACE_MINUTES = '60';

    memdb = new Database(':memory:');
    initializeDatabase(memdb);

    // Inyectar DB en servicios
    setDb(memdb);

    // Limpiar tablas entre tests por seguridad
    memdb.exec(`DELETE FROM response_queue;`);
    memdb.exec(`DELETE FROM user_preferences;`);
    memdb.exec(`DELETE FROM users;`);
    memdb.exec(`DELETE FROM tasks;`);
    memdb.exec(`DELETE FROM task_assignments;`);

    // Asegurar usuario
    memdb.exec(`
      INSERT INTO users (id, first_seen, last_seen)
      VALUES ('${USER}', '${toIso(new Date())}', '${toIso(new Date())}')
      ON CONFLICT(id) DO NOTHING;
    `);
  });

  function insertPref(freq: 'daily' | 'weekly' | 'weekdays' | 'off', time: string = '08:30', last: string | null = null) {
    memdb.prepare(`
      INSERT INTO user_preferences (user_id, reminder_freq, reminder_time, last_reminded_on, updated_at)
      VALUES (?, ?, ?, ?, strftime('%Y-%m-%d %H:%M:%f', 'now'))
      ON CONFLICT(user_id) DO UPDATE SET
        reminder_freq = excluded.reminder_freq,
        reminder_time = excluded.reminder_time,
        last_reminded_on = excluded.last_reminded_on,
        updated_at = excluded.updated_at
    `).run(USER, freq, time, last);
  }

  function countQueued(): number {
    const row = memdb.prepare(`SELECT COUNT(*) AS c FROM response_queue`).get() as any;
    return Number(row?.c || 0);
  }

  function getLastReminded(): string | null {
    const row = memdb.prepare(`SELECT last_reminded_on AS d FROM user_preferences WHERE user_id = ?`).get(USER) as any;
    return row?.d ?? null;
  }

  it('envía recordatorio diario cuando hora alcanzada y no enviado hoy', async () => {
    insertPref('daily', '08:30', null);
    // Lunes 2025-09-08 09:30 Europe/Madrid ≈ 07:30Z
    const now = new Date('2025-09-08T07:30:00.000Z');

    // Crear 1 tarea asignada al usuario
    TaskService.createTask(
      { description: 'Tarea A', due_date: '2025-09-10', group_id: null, created_by: USER },
      [{ user_id: USER, assigned_by: USER }]
    );

    await RemindersService.runOnce(now);

    expect(countQueued()).toBe(1);
    expect(getLastReminded()).toBe('2025-09-08'); // YYYY-MM-DD en TZ
  });

  it('no duplica recordatorio diario en el mismo día', async () => {
    insertPref('daily', '08:30', null);
    const now = new Date('2025-09-08T07:30:00.000Z'); // dentro de ventana (≤ 60 min)

    TaskService.createTask(
      { description: 'Tarea B', due_date: '2025-09-11', group_id: null, created_by: USER },
      [{ user_id: USER, assigned_by: USER }]
    );

    await RemindersService.runOnce(now);
    expect(countQueued()).toBe(1);

    // Segunda ejecución el mismo día (fuera de ventana, pero ya enviado hoy → no duplica)
    await RemindersService.runOnce(new Date('2025-09-08T08:00:00.000Z'));
    expect(countQueued()).toBe(1); // sin incremento
  });

  it('no envía diario antes de la hora configurada', async () => {
    insertPref('daily', '08:30', null);
    const now = new Date('2025-09-08T05:00:00.000Z'); // 07:00 local < 08:30

    TaskService.createTask(
      { description: 'Tarea C', due_date: '2025-09-12', group_id: null, created_by: USER },
      [{ user_id: USER, assigned_by: USER }]
    );

    await RemindersService.runOnce(now);
    expect(countQueued()).toBe(0);
    expect(getLastReminded()).toBeNull();
  });

  it('no envía si el usuario no tiene tareas; no marca last_reminded_on', async () => {
    insertPref('daily', '08:30', null);
    const now = new Date('2025-09-08T07:40:00.000Z'); // ≥ 08:30 local

    await RemindersService.runOnce(now);
    expect(countQueued()).toBe(0);
    expect(getLastReminded()).toBeNull();
  });

  it('weekly: solo envía en lunes y a la hora, no en martes', async () => {
    insertPref('weekly', '08:30', null);

    // Crear 1 tarea
    TaskService.createTask(
      { description: 'Tarea W', due_date: '2025-09-15', group_id: null, created_by: USER },
      [{ user_id: USER, assigned_by: USER }]
    );

    // Lunes (Mon) 2025-09-08 07:00 local (05:00Z) -> antes de hora: no envía
    await RemindersService.runOnce(new Date('2025-09-08T05:00:00.000Z'));
    expect(countQueued()).toBe(0);
    expect(getLastReminded()).toBeNull();

    // Lunes 09:30 local (07:30Z) -> envía
    await RemindersService.runOnce(new Date('2025-09-08T07:30:00.000Z'));
    expect(countQueued()).toBe(1);
    expect(getLastReminded()).toBe('2025-09-08');

    // Martes 2025-09-09 ≥ hora -> no debe enviar (weekly solo lunes)
    await RemindersService.runOnce(new Date('2025-09-09T07:40:00.000Z'));
    expect(countQueued()).toBe(1); // sin incremento
    // last_reminded_on permanece en lunes
    expect(getLastReminded()).toBe('2025-09-08');
  });

  it('incluye “… y X más” cuando hay más de 10 tareas (tope de listado)', async () => {
    insertPref('daily', '08:30', null);
    const now = new Date('2025-09-08T07:30:00.000Z'); // dentro de ventana

    // Crear 12 tareas asignadas al usuario
    for (let i = 0; i < 12; i++) {
      TaskService.createTask(
        { description: `Tarea ${i + 1}`, due_date: '2025-09-20', group_id: null, created_by: USER },
        [{ user_id: USER, assigned_by: USER }]
      );
    }

    await RemindersService.runOnce(now);
    expect(countQueued()).toBe(1);

    const row = memdb.prepare(`SELECT message FROM response_queue LIMIT 1`).get() as any;
    const msg: string = String(row?.message || '');
    expect(msg.includes('… y 2 más')).toBe(true);
  });

  it('weekdays: envía en martes a la hora configurada', async () => {
    insertPref('weekdays', '08:00', null);
    // Martes 2025-09-09 08:05 Europe/Madrid ≈ 06:05Z
    const now = new Date('2025-09-09T06:05:00.000Z');

    // Crear 1 tarea asignada al usuario
    TaskService.createTask(
      { description: 'Tarea LV', due_date: '2025-09-10', group_id: null, created_by: USER },
      [{ user_id: USER, assigned_by: USER }]
    );

    await RemindersService.runOnce(now);
    expect(countQueued()).toBe(1);
    expect(getLastReminded()).toBe('2025-09-09');
  });

  it('weekdays: no envía en sábado', async () => {
    insertPref('weekdays', '08:00', null);
    // Sábado 2025-09-13 08:05 Europe/Madrid ≈ 06:05Z
    const now = new Date('2025-09-13T06:05:00.000Z');

    TaskService.createTask(
      { description: 'Tarea LV2', due_date: '2025-09-14', group_id: null, created_by: USER },
      [{ user_id: USER, assigned_by: USER }]
    );

    await RemindersService.runOnce(now);
    expect(countQueued()).toBe(0);
    expect(getLastReminded()).toBeNull();
  });

  it('no envía fuera de la ventana de gracia tras reinicio tardío', async () => {
    insertPref('daily', '08:30', null);
    // 08:30 local + 90 min ≈ 10:00 local => 08:00Z en CEST
    const now = new Date('2025-09-08T08:00:00.000Z');

    // Crear una tarea asignada después de la hora configurada
    TaskService.createTask(
      { description: 'Tarea fuera de ventana', due_date: '2025-09-20', group_id: null, created_by: USER },
      [{ user_id: USER, assigned_by: USER }]
    );

    await RemindersService.runOnce(now);
    expect(countQueued()).toBe(0);
    expect(getLastReminded()).toBeNull();
  });

  it('envía dentro de la ventana de gracia si hay tareas', async () => {
    insertPref('daily', '08:30', null);
    // 08:30 local + 45 min ≈ 09:15 local => 07:15Z en CEST
    const now = new Date('2025-09-08T07:15:00.000Z');

    TaskService.createTask(
      { description: 'Tarea dentro de ventana', due_date: '2025-09-20', group_id: null, created_by: USER },
      [{ user_id: USER, assigned_by: USER }]
    );

    await RemindersService.runOnce(now);
    expect(countQueued()).toBe(1);
    expect(getLastReminded()).toBe('2025-09-08');
  });
});
