const request = require('supertest');
const { app } = require('../../backend/server');
const { getAuthHeader, cleanDb, seedGame, seedSession, seedSessionGame, db } = require('../helpers/test-utils');

describe('POST /api/votes/live -> read-back (end-to-end)', () => {
  let game1, game2, session;
  const baseTime = '2026-03-15T20:00:00.000Z';
  const game2Time = '2026-03-15T20:30:00.000Z';

  beforeEach(() => {
    cleanDb();
    game1 = seedGame({ title: 'Quiplash 3', pack_name: 'Party Pack 7' });
    game2 = seedGame({ title: 'Drawful 2', pack_name: 'Party Pack 3' });
    session = seedSession({ is_active: 1 });
    seedSessionGame(session.id, game1.id, { status: 'played', played_at: baseTime });
    seedSessionGame(session.id, game2.id, { status: 'playing', played_at: game2Time });
  });

  test('vote via POST populates live_votes table (direct DB check)', async () => {
    const res = await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer1',
        vote: 'up',
        timestamp: '2026-03-15T20:35:00.000Z',
      });

    expect(res.status).toBe(200);

    const row = db.prepare(
      'SELECT * FROM live_votes WHERE session_id = ? AND game_id = ? AND username = ?'
    ).get(session.id, game2.id, 'viewer1');

    expect(row).toBeDefined();
    expect(row.session_id).toBe(session.id);
    expect(row.game_id).toBe(game2.id);
    expect(row.username).toBe('viewer1');
    expect(row.vote_type).toBe(1);
    expect(row.timestamp).toBe('2026-03-15T20:35:00.000Z');
  });

  test('vote via POST is visible in GET /api/sessions/:id/votes', async () => {
    await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer1',
        vote: 'up',
        timestamp: '2026-03-15T20:35:00.000Z',
      });

    await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer2',
        vote: 'down',
        timestamp: '2026-03-15T20:36:00.000Z',
      });

    const res = await request(app).get(`/api/sessions/${session.id}/votes`);

    expect(res.status).toBe(200);
    expect(res.body.session_id).toBe(session.id);
    expect(res.body.votes).toHaveLength(1);

    const gameVotes = res.body.votes[0];
    expect(gameVotes.game_id).toBe(game2.id);
    expect(gameVotes.title).toBe('Drawful 2');
    expect(gameVotes.upvotes).toBe(1);
    expect(gameVotes.downvotes).toBe(1);
    expect(gameVotes.net_score).toBe(0);
    expect(gameVotes.total_votes).toBe(2);
  });

  test('vote via POST is visible in GET /api/votes', async () => {
    await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer1',
        vote: 'up',
        timestamp: '2026-03-15T20:35:00.000Z',
      });

    const res = await request(app).get(`/api/votes?session_id=${session.id}`);

    expect(res.status).toBe(200);
    expect(res.body.votes).toHaveLength(1);
    expect(res.body.votes[0].username).toBe('viewer1');
    expect(res.body.votes[0].vote_type).toBe('up');
    expect(res.body.votes[0].game_id).toBe(game2.id);
    expect(res.body.votes[0].game_title).toBe('Drawful 2');
    expect(res.body.votes[0].session_id).toBe(session.id);
  });

  test('votes for different games in same session are tracked separately', async () => {
    await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer1',
        vote: 'up',
        timestamp: '2026-03-15T20:10:00.000Z',
      });

    await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer2',
        vote: 'down',
        timestamp: '2026-03-15T20:35:00.000Z',
      });

    const res = await request(app).get(`/api/sessions/${session.id}/votes`);

    expect(res.status).toBe(200);
    expect(res.body.votes).toHaveLength(2);

    const q3 = res.body.votes.find((v) => v.game_id === game1.id);
    expect(q3.upvotes).toBe(1);
    expect(q3.downvotes).toBe(0);

    const d2 = res.body.votes.find((v) => v.game_id === game2.id);
    expect(d2.upvotes).toBe(0);
    expect(d2.downvotes).toBe(1);
  });

  test('live_votes row count matches number of accepted votes', async () => {
    const timestamps = [
      '2026-03-15T20:35:00.000Z',
      '2026-03-15T20:36:05.000Z',
      '2026-03-15T20:37:10.000Z',
    ];

    for (let i = 0; i < timestamps.length; i++) {
      const res = await request(app)
        .post('/api/votes/live')
        .set('Authorization', getAuthHeader())
        .set('Content-Type', 'application/json')
        .send({
          username: `viewer${i}`,
          vote: i % 2 === 0 ? 'up' : 'down',
          timestamp: timestamps[i],
        });
      expect(res.status).toBe(200);
    }

    const count = db.prepare(
      'SELECT COUNT(*) as cnt FROM live_votes WHERE session_id = ?'
    ).get(session.id);
    expect(count.cnt).toBe(3);

    const sessionVotes = await request(app).get(`/api/sessions/${session.id}/votes`);
    expect(sessionVotes.body.votes[0].total_votes).toBe(3);
  });
});

describe('POST /api/votes/live — ticker voting', () => {
  let session, tickerGame, sessionGame;
  const baseTime = '2026-03-15T20:00:00.000Z';

  beforeEach(() => {
    cleanDb();
    tickerGame = seedGame({ title: 'Quiplash 3', pack_name: 'Party Pack 7', ticker: 'QPL3' });
    sessionGame = seedGame({ title: 'Drawful 2', pack_name: 'Party Pack 3' });
    session = seedSession({ is_active: 1 });
    seedSessionGame(session.id, sessionGame.id, { status: 'playing', played_at: baseTime });
  });

  test('vote with valid ticker resolves to the correct game', async () => {
    const res = await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer1',
        vote: 'up',
        timestamp: '2026-03-15T20:05:00.000Z',
        ticker: 'QPL3',
      });

    expect(res.status).toBe(200);
    expect(res.body.game.id).toBe(tickerGame.id);
    expect(res.body.game.title).toBe('Quiplash 3');
    expect(res.body.vote.ticker).toBe('QPL3');

    const row = db.prepare(
      'SELECT * FROM live_votes WHERE game_id = ? AND username = ?'
    ).get(tickerGame.id, 'viewer1');
    expect(row).toBeDefined();
    expect(row.vote_type).toBe(1);
  });

  test('ticker vote works for a game not in the active session', async () => {
    const outsideGame = seedGame({ title: 'Fibbage XL', pack_name: 'Party Pack 1', ticker: 'FBXL' });

    const res = await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer1',
        vote: 'down',
        timestamp: '2026-03-15T20:05:00.000Z',
        ticker: 'FBXL',
      });

    expect(res.status).toBe(200);
    expect(res.body.game.id).toBe(outsideGame.id);
    expect(res.body.game.title).toBe('Fibbage XL');
  });

  test('unknown ticker returns 404', async () => {
    const res = await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer1',
        vote: 'up',
        timestamp: '2026-03-15T20:05:00.000Z',
        ticker: 'NOPE',
      });

    expect(res.status).toBe(404);
    expect(res.body.error).toMatch(/Unknown ticker 'NOPE'/);
  });

  test('ticker vote updates game scores', async () => {
    await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer1',
        vote: 'up',
        timestamp: '2026-03-15T20:05:00.000Z',
        ticker: 'QPL3',
      });

    await request(app)
      .post('/api/votes/live')
      .set('Authorization', getAuthHeader())
      .set('Content-Type', 'application/json')
      .send({
        username: 'viewer2',
        vote: 'down',
        timestamp: '2026-03-15T20:06:05.000Z',
        ticker: 'QPL3',
      });

    const game = db.prepare('SELECT upvotes, downvotes, popularity_score FROM games WHERE id = ?').get(tickerGame.id);
    expect(game.upvotes).toBe(1);
    expect(game.downvotes).toBe(1);
    expect(game.popularity_score).toBe(0);
  });
});
