const path = require('path');
const fs = require('fs');
const os = require('os');

describe('load-admins', () => {
  const originalEnv = { ...process.env };
  let tmpDir;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'admins-test-'));
    delete process.env.ADMIN_CONFIG_PATH;
    delete process.env.ADMIN_KEY;
  });

  afterEach(() => {
    process.env = { ...originalEnv };
    fs.rmSync(tmpDir, { recursive: true, force: true });
    jest.resetModules();
  });

  function writeConfig(admins) {
    const filePath = path.join(tmpDir, 'admins.json');
    fs.writeFileSync(filePath, JSON.stringify(admins));
    return filePath;
  }

  test('loads admins from ADMIN_CONFIG_PATH', () => {
    const configPath = writeConfig([
      { name: 'Alice', role: 'admin', key: 'key-a' },
      { name: 'Bob', role: 'bot', key: 'key-b' }
    ]);
    process.env.ADMIN_CONFIG_PATH = configPath;

    const { findAdminByKey } = require('../../backend/config/load-admins');
    expect(findAdminByKey('key-a')).toEqual({ name: 'Alice', role: 'admin' });
    expect(findAdminByKey('key-b')).toEqual({ name: 'Bob', role: 'bot' });
    expect(findAdminByKey('wrong')).toBeNull();
  });

  test('defaults role to admin when not specified', () => {
    const configPath = writeConfig([
      { name: 'Alice', key: 'key-a' }
    ]);
    process.env.ADMIN_CONFIG_PATH = configPath;

    const { findAdminByKey } = require('../../backend/config/load-admins');
    expect(findAdminByKey('key-a')).toEqual({ name: 'Alice', role: 'admin' });
  });

  test('falls back to ADMIN_KEY when no config file', () => {
    process.env.ADMIN_CONFIG_PATH = path.join(tmpDir, 'nonexistent.json');
    process.env.ADMIN_KEY = 'legacy-key';

    const { findAdminByKey } = require('../../backend/config/load-admins');
    expect(findAdminByKey('legacy-key')).toEqual({ name: 'Admin', role: 'admin' });
    expect(findAdminByKey('wrong')).toBeNull();
  });

  test('throws when neither config file nor ADMIN_KEY exists', () => {
    process.env.ADMIN_CONFIG_PATH = path.join(tmpDir, 'nonexistent.json');

    expect(() => {
      require('../../backend/config/load-admins');
    }).toThrow();
  });

  test('rejects duplicate admin names', () => {
    const configPath = writeConfig([
      { name: 'Alice', key: 'key-a' },
      { name: 'Alice', key: 'key-b' }
    ]);
    process.env.ADMIN_CONFIG_PATH = configPath;

    expect(() => {
      require('../../backend/config/load-admins');
    }).toThrow(/duplicate/i);
  });

  test('rejects duplicate keys', () => {
    const configPath = writeConfig([
      { name: 'Alice', key: 'same-key' },
      { name: 'Bob', key: 'same-key' }
    ]);
    process.env.ADMIN_CONFIG_PATH = configPath;

    expect(() => {
      require('../../backend/config/load-admins');
    }).toThrow(/duplicate/i);
  });
});

const request = require('supertest');

describe('POST /api/auth/login — named admins', () => {
  let app;

  beforeAll(() => {
    process.env.ADMIN_KEY = 'test-admin-key';
    process.env.ADMIN_CONFIG_PATH = '/tmp/nonexistent-admins.json';
    jest.resetModules();
    ({ app } = require('../../backend/server'));
  });

  test('login returns admin name and role in response', async () => {
    const res = await request(app)
      .post('/api/auth/login')
      .set('Content-Type', 'application/json')
      .send({ key: 'test-admin-key' });

    expect(res.status).toBe(200);
    expect(res.body.name).toBeDefined();
    expect(res.body.role).toBe('admin');
    expect(res.body.token).toBeDefined();
  });

  test('verify returns admin name and role in user object', async () => {
    const loginRes = await request(app)
      .post('/api/auth/login')
      .set('Content-Type', 'application/json')
      .send({ key: 'test-admin-key' });

    const res = await request(app)
      .post('/api/auth/verify')
      .set('Authorization', `Bearer ${loginRes.body.token}`);

    expect(res.status).toBe(200);
    expect(res.body.user.name).toBeDefined();
    expect(res.body.user.role).toBe('admin');
  });

  test('invalid key still returns 401', async () => {
    const res = await request(app)
      .post('/api/auth/login')
      .set('Content-Type', 'application/json')
      .send({ key: 'wrong-key' });

    expect(res.status).toBe(401);
  });
});

const WebSocket = require('ws');
const jwt = require('jsonwebtoken');
const http = require('http');

describe('WebSocket presence', () => {
  let server, wsUrl;

  beforeAll((done) => {
    process.env.ADMIN_KEY = 'test-admin-key';
    process.env.ADMIN_CONFIG_PATH = '/tmp/nonexistent-admins.json';
    jest.resetModules();
    const { app } = require('../../backend/server');
    const { WebSocketManager, setWebSocketManager } = require('../../backend/utils/websocket-manager');

    server = http.createServer(app);
    const wsManager = new WebSocketManager(server);
    setWebSocketManager(wsManager);

    server.listen(0, () => {
      const port = server.address().port;
      wsUrl = `ws://localhost:${port}/api/sessions/live`;
      done();
    });
  });

  afterAll((done) => {
    server.close(done);
  });

  function makeToken(name, role = 'admin') {
    return jwt.sign({ role, name }, process.env.JWT_SECRET, { expiresIn: '1h' });
  }

  function connectAndAuth(name, role = 'admin') {
    return new Promise((resolve) => {
      const ws = new WebSocket(wsUrl);
      ws.on('open', () => {
        ws.send(JSON.stringify({ type: 'auth', token: makeToken(name, role) }));
      });
      ws.on('message', (data) => {
        const msg = JSON.parse(data.toString());
        if (msg.type === 'auth_success') {
          resolve(ws);
        }
      });
    });
  }

  function waitForMessage(ws, type) {
    return new Promise((resolve) => {
      const handler = (data) => {
        const msg = JSON.parse(data.toString());
        if (msg.type === type) {
          ws.off('message', handler);
          resolve(msg);
        }
      };
      ws.on('message', handler);
    });
  }

  test('page_focus triggers presence_update with admin name, role, and page', async () => {
    const ws1 = await connectAndAuth('Alice');
    const ws2 = await connectAndAuth('Bob');

    const presencePromise = waitForMessage(ws2, 'presence_update');

    ws1.send(JSON.stringify({ type: 'page_focus', page: '/history' }));

    const msg = await presencePromise;
    expect(msg.admins).toEqual(
      expect.arrayContaining([
        expect.objectContaining({ name: 'Alice', role: 'admin', page: '/history' })
      ])
    );

    ws1.close();
    ws2.close();
  });

  test('disconnect removes admin from presence', async () => {
    const ws1 = await connectAndAuth('Alice');
    const ws2 = await connectAndAuth('Bob');

    ws1.send(JSON.stringify({ type: 'page_focus', page: '/picker' }));
    ws2.send(JSON.stringify({ type: 'page_focus', page: '/picker' }));

    await new Promise(r => setTimeout(r, 100));

    const presencePromise = waitForMessage(ws2, 'presence_update');
    ws1.close();

    const msg = await presencePromise;
    const names = msg.admins.map(a => a.name);
    expect(names).not.toContain('Alice');

    ws2.close();
  });

  test('bot role appears in presence without page_focus', async () => {
    const wsAdmin = await connectAndAuth('Alice');
    const wsBot = await connectAndAuth('ChatBot', 'bot');

    wsAdmin.send(JSON.stringify({ type: 'page_focus', page: '/history' }));

    await new Promise(r => setTimeout(r, 100));

    const presencePromise = waitForMessage(wsAdmin, 'presence_update');
    wsAdmin.send(JSON.stringify({ type: 'page_focus', page: '/picker' }));

    const msg = await presencePromise;
    const botEntry = msg.admins.find(a => a.name === 'ChatBot');
    expect(botEntry).toBeDefined();
    expect(botEntry.role).toBe('bot');
    expect(botEntry.page).toBeNull();

    wsAdmin.close();
    wsBot.close();
  });

  test('utility role appears in presence without page_focus', async () => {
    const wsAdmin = await connectAndAuth('Alice');
    const wsUtil = await connectAndAuth('OBS', 'utility');

    wsAdmin.send(JSON.stringify({ type: 'page_focus', page: '/history' }));

    await new Promise(r => setTimeout(r, 100));

    const presencePromise = waitForMessage(wsAdmin, 'presence_update');
    wsAdmin.send(JSON.stringify({ type: 'page_focus', page: '/picker' }));

    const msg = await presencePromise;
    const utilEntry = msg.admins.find(a => a.name === 'OBS');
    expect(utilEntry).toBeDefined();
    expect(utilEntry.role).toBe('utility');
    expect(utilEntry.page).toBeNull();

    wsAdmin.close();
    wsUtil.close();
  });

  test('admin without page_focus is NOT in presence', async () => {
    const ws1 = await connectAndAuth('Alice');
    const ws2 = await connectAndAuth('Bob');

    const presencePromise = waitForMessage(ws1, 'presence_update');
    ws1.send(JSON.stringify({ type: 'page_focus', page: '/history' }));

    const msg = await presencePromise;
    const bobEntry = msg.admins.find(a => a.name === 'Bob');
    expect(bobEntry).toBeUndefined();

    ws1.close();
    ws2.close();
  });
});
