/**
 * End-to-end check that POST /api/messages writes notifications for the
 * recipient. Uses an isolated SQLite DB and mounts the messages router
 * with a stub wsHub so we don't need the full Express bootstrap.
 */
const express = require('express');
const request = require('supertest');
const bcrypt = require('bcryptjs');
const Database = require('better-sqlite3');
const path = require('path');
const os = require('os');
const { jwtSecret: JWT_SECRET } = require('../src/config');

function makeDb() {
  const tmp = path.join(os.tmpdir(), `msg-notif-${Date.now()}.db`);
  const db = new Database(tmp);
  db.pragma('foreign_keys = ON');
  db.exec(`
    CREATE TABLE users (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      uid TEXT UNIQUE,
      email TEXT,
      password TEXT,
      role TEXT,
      first_name TEXT,
      last_name TEXT,
      is_active INTEGER DEFAULT 1,
      is_deleted INTEGER DEFAULT 0
    );
    CREATE TABLE messages (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      uid TEXT UNIQUE,
      sender_id INTEGER,
      recipient_id INTEGER,
      subject TEXT NOT NULL,
      body TEXT,
      is_read INTEGER DEFAULT 0,
      is_delivered INTEGER DEFAULT 0,
      is_announcement INTEGER DEFAULT 0,
      priority TEXT DEFAULT 'normal',
      group_id INTEGER,
      attachment_url TEXT,
      attachment_name TEXT,
      attachment_type TEXT,
      created_at DATETIME DEFAULT (datetime('now','localtime')),
      updated_at DATETIME DEFAULT (datetime('now','localtime')),
      last_synced_at DATETIME,
      sync_status TEXT DEFAULT 'pending',
      is_deleted INTEGER DEFAULT 0
    );
    CREATE TABLE notifications (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      uid TEXT UNIQUE,
      recipient_id INTEGER NOT NULL REFERENCES users(id),
      type TEXT NOT NULL CHECK (type IN ('in_app','email','sms')),
      subject TEXT,
      body TEXT,
      related_resource TEXT,
      status TEXT NOT NULL DEFAULT 'pending'
        CHECK (status IN ('pending','sent','failed','read')),
      sent_at DATETIME,
      read_at DATETIME,
      error_message TEXT,
      metadata TEXT,
      created_at DATETIME DEFAULT (datetime('now','localtime')),
      updated_at DATETIME DEFAULT (datetime('now','localtime')),
      last_synced_at DATETIME,
      sync_status TEXT DEFAULT 'pending',
      is_deleted INTEGER DEFAULT 0
    );
  `);
  const hash = bcrypt.hashSync('secret123', 4);
  const alice = db.prepare(`INSERT INTO users (uid,email,password,role,first_name,last_name) VALUES (?,?,?,?,?,?)`)
    .run('u-alice', 'alice@x', hash, 'teacher', 'Alice', 'A');
  const bob = db.prepare(`INSERT INTO users (uid,email,password,role,first_name,last_name) VALUES (?,?,?,?,?,?)`)
    .run('u-bob', 'bob@x', hash, 'teacher', 'Bob', 'B');
  return { db, aliceId: alice.lastInsertRowid, bobId: bob.lastInsertRowid };
}

function buildApp(db) {
  process.env.DB_PATH = db.name;
  // Force the controllers to re-import against the swapped DB path.
  for (const p of [
    require.resolve('../src/controllers/notifications.controller'),
    require.resolve('../src/controllers/messages.controller'),
  ]) {
    delete require.cache[p];
  }
  const messages = require('../src/controllers/messages.controller');

  const app = express();
  app.use(express.json());
  // Stub wsHub so the controller can call .broadcastToUser without crashing.
  app.set('wsHub', { broadcastToUser: () => {} });
  app.use('/api/messages', messages);
  return app;
}

function token(id, role) {
  const jwt = require('jsonwebtoken');
  return jwt.sign({ id, email: 'x@x', role }, JWT_SECRET, { expiresIn: '5m' });
}

describe('POST /api/messages creates a notification for the recipient', () => {
  let app;
  let db;
  let aliceToken;
  let bobToken;
  let aliceId;
  let bobId;

  beforeAll(() => {
    const built = makeDb();
    db = built.db;
    aliceId = built.aliceId;
    bobId = built.bobId;
    app = buildApp(db);
    aliceToken = token(aliceId, 'teacher');
    bobToken = token(bobId, 'teacher');
  });

  afterAll(() => {
    db.close();
  });

  test('direct message → recipient gets a notification, sender does not', async () => {
    const res = await request(app)
      .post('/api/messages')
      .set('Authorization', `Bearer ${aliceToken}`)
      .send({ recipient_id: bobId, subject: 'Hello', body: 'hi bob' });
    expect(res.status).toBe(201);

    const bobInbox = db.prepare('SELECT * FROM notifications WHERE recipient_id = ?').all(bobId);
    expect(bobInbox.length).toBe(1);
    expect(bobInbox[0].subject).toBe('Hello');
    expect(bobInbox[0].related_resource).toMatch(/^message:\d+$/);

    const aliceInbox = db.prepare('SELECT * FROM notifications WHERE recipient_id = ?').all(aliceId);
    expect(aliceInbox.length).toBe(0);
  });

  test('400 when recipient_id is missing', async () => {
    const res = await request(app)
      .post('/api/messages')
      .set('Authorization', `Bearer ${aliceToken}`)
      .send({ subject: 'no recipient' });
    expect(res.status).toBe(400);
  });
});