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 { sha256Hex } from '../../../src/utils/crypto';
import { Metrics } from '../../../src/services/metrics';
import { setDb } from '../../../src/db/locator';

const envBackup = { ...process.env };
let memdb: Database;

describe('CommandService - t web (emisión de token de login)', () => {
  beforeEach(() => {
    process.env = {
      ...envBackup,
      NODE_ENV: 'test',
      TZ: 'Europe/Madrid',
      WEB_BASE_URL: 'https://app.example.test',
      METRICS_ENABLED: 'true'
    };
    Metrics.reset?.();
    memdb = new Database(':memory:');
    initializeDatabase(memdb);
    setDb(memdb);
  });

  afterEach(() => {
    process.env = envBackup;
    try { memdb.close(); } catch {}
  });

  test('DM feliz: devuelve URL con token y persiste hash en web_tokens', async () => {
    const res = await CommandService.handle({
      sender: '34600123456',
      groupId: '34600123456@s.whatsapp.net', // DM (no @g.us)
      message: 't web',
      mentions: []
    });
    expect(Array.isArray(res)).toBe(true);
    expect(res.length).toBe(1);
    expect(res[0].recipient).toBe('34600123456');
    expect(res[0].message).toContain('https://app.example.test');

    const m = res[0].message.match(/https?:\/\/\S+/);
    expect(m).toBeTruthy();
    const url = new URL(m![0]);
    expect(url.pathname).toBe('/login');
    const token = url.searchParams.get('token') || '';
    expect(token.length).toBeGreaterThan(0);

    const hash = await sha256Hex(token);
    const row = memdb.prepare(`
      SELECT user_id, token_hash, used_at, expires_at
      FROM web_tokens
      WHERE user_id = ? AND token_hash = ?
    `).get('34600123456', hash) as any;

    expect(row).toBeTruthy();
    expect(row.user_id).toBe('34600123456');
    expect(row.token_hash).toBe(hash);
    expect(row.used_at).toBeNull();
    // expires_at debe ser en el futuro
    const now = new Date();
    const exp = new Date(String(row.expires_at).replace(' ', 'T') + 'Z');
    expect(exp.getTime()).toBeGreaterThan(now.getTime() + 9 * 60 * 1000 - 10 * 1000); // ~>= 9min50s
    expect(Metrics.get('web_tokens_issued_total')).toBe(1);
  });

  test('En grupo: responde que debe usarse por privado y no inserta token', async () => {
    const res = await CommandService.handle({
      sender: '34600123456',
      groupId: '123@g.us',
      message: 't web',
      mentions: []
    });
    expect(res.length).toBe(1);
    expect(res[0].recipient).toBe('34600123456');
    expect(res[0].message.toLowerCase()).toContain('privado');

    const cnt = memdb.prepare(`SELECT COUNT(*) AS c FROM web_tokens WHERE user_id = ?`).get('34600123456') as any;
    expect(Number(cnt.c)).toBe(0);
  });

  test('Sin WEB_BASE_URL: error claro y no inserta token', async () => {
    delete process.env.WEB_BASE_URL;

    const res = await CommandService.handle({
      sender: '34600123456',
      groupId: '34600123456@s.whatsapp.net',
      message: 't web',
      mentions: []
    });
    expect(res.length).toBe(1);
    expect(res[0].message).toContain('no está configurada');
    const cnt = memdb.prepare(`SELECT COUNT(*) AS c FROM web_tokens WHERE user_id = ?`).get('34600123456') as any;
    expect(Number(cnt.c)).toBe(0);
  });

  test('Token vigente: se invalida y se emite uno nuevo (queda solo 1 activo)', async () => {
    // Primera emisión
    {
      const r1 = await CommandService.handle({
        sender: '34600123456',
        groupId: '34600123456@s.whatsapp.net',
        message: 't web',
        mentions: []
      });
      expect(r1.length).toBe(1);
    }

    // Segunda emisión (debe invalidar el anterior)
    {
      const r2 = await CommandService.handle({
        sender: '34600123456',
        groupId: '34600123456@s.whatsapp.net',
        message: 't web',
        mentions: []
      });
      expect(r2.length).toBe(1);
    }

    const counts = memdb.prepare(`
      SELECT
        SUM(CASE WHEN used_at IS NULL THEN 1 ELSE 0 END) AS active,
        COUNT(*) AS total
      FROM web_tokens
      WHERE user_id = ?
    `).get('34600123456') as any;

    expect(Number(counts.total)).toBeGreaterThanOrEqual(2);
    expect(Number(counts.active)).toBe(1);
  });
});
