import { describe, it, expect, beforeEach } from 'vitest';
import type Database from 'better-sqlite3';
import { createPulse, castVote, getUserVote, countPulseParticipants } from '../src/lib/db.js';

const db = (globalThis as typeof globalThis & { __bifrost_db?: Database.Database }).__bifrost_db!;

function seedUser(name: string, role: 'pilot' | 'cab' | 'fenja' = 'cab'): number {
  const r = db.prepare(
    'INSERT INTO users (email, password_hash, name, organisation, role, slug) VALUES (?,?,?,?,?,?)'
  ).run(`${name}@example.test`, 'x', name, 'TestOrg', role, name.toLowerCase());
  return Number(r.lastInsertRowid);
}

function seedPulse(createdBy: number): number {
  return createPulse({
    question: 'Should we ship X?',
    context: null,
    options: ['Yes', 'No'],
    opens_at: new Date(Date.now() - 60_000).toISOString(),
    closes_at: new Date(Date.now() + 60 * 60_000).toISOString(),
    status: 'open',
    created_by: createdBy,
  });
}

beforeEach(() => {
  db.exec(`
    DELETE FROM votes;
    DELETE FROM pulses;
    DELETE FROM activity;
    DELETE FROM users;
  `);
});

describe('vote uniqueness (UNIQUE(pulse_id, user_id))', () => {
  it('rejects a second vote by the same user on the same pulse (first wins)', () => {
    const admin = seedUser('admin', 'fenja');
    const voter = seedUser('voter', 'cab');
    const pulseId = seedPulse(admin);

    castVote(pulseId, voter, 0);
    // INSERT OR IGNORE → silent no-op for the duplicate.
    castVote(pulseId, voter, 1);

    expect(getUserVote(pulseId, voter)).toBe(0);
    expect(countPulseParticipants(pulseId)).toBe(1);
  });

  it('raises a SqliteError when bypassing OR IGNORE with a raw INSERT', () => {
    const admin = seedUser('admin', 'fenja');
    const voter = seedUser('voter', 'cab');
    const pulseId = seedPulse(admin);
    castVote(pulseId, voter, 0);

    expect(() =>
      db.prepare('INSERT INTO votes (pulse_id, user_id, option_index) VALUES (?,?,?)')
        .run(pulseId, voter, 1)
    ).toThrow(/UNIQUE constraint failed/);
  });

  it('allows two different users to vote on the same pulse', () => {
    const admin = seedUser('admin', 'fenja');
    const a = seedUser('alice', 'cab');
    const b = seedUser('bob',   'cab');
    const pulseId = seedPulse(admin);

    castVote(pulseId, a, 0);
    castVote(pulseId, b, 1);

    expect(countPulseParticipants(pulseId)).toBe(2);
    expect(getUserVote(pulseId, a)).toBe(0);
    expect(getUserVote(pulseId, b)).toBe(1);
  });

  it('allows the same user to vote on two different pulses', () => {
    const admin = seedUser('admin', 'fenja');
    const voter = seedUser('voter', 'cab');
    const p1 = seedPulse(admin);
    const p2 = seedPulse(admin);

    castVote(p1, voter, 0);
    castVote(p2, voter, 1);

    expect(getUserVote(p1, voter)).toBe(0);
    expect(getUserVote(p2, voter)).toBe(1);
  });
});
