/**
 * Route-level tests for votes module — Fastify inject.
 */

import Fastify from 'fastify';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const voteRepoMock = {
  getByItemAndUser: vi.fn(),
  create: vi.fn(),
  remove: vi.fn(),
  countByItem: vi.fn(),
  listByItem: vi.fn(),
};

const itemRepoMock = {
  getById: vi.fn(),
  updateVoteCount: vi.fn(),
};

vi.mock('./repository.js', () => voteRepoMock);
vi.mock('../items/repository.js', () => itemRepoMock);
vi.mock('../../lib/auth.js', () => ({
  extractAuth: vi.fn(async () => ({ sub: 'user_1', role: 'user' })),
}));

const baseItem = {
  id: 'item_1',
  productId: 'lysnrai',
  title: 'Improve transcription speed',
  visibility: 'public',
};

describe('voteRoutes', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('POST /items/:itemId/vote adds vote when none exists', async () => {
    itemRepoMock.getById.mockResolvedValue(baseItem);
    voteRepoMock.getByItemAndUser.mockResolvedValue(null);
    voteRepoMock.countByItem.mockResolvedValue(3);

    const { voteRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(voteRoutes, { prefix: '/api' });

    const res = await app.inject({ method: 'POST', url: '/api/items/item_1/vote' });

    expect(res.statusCode).toBe(200);
    const data = JSON.parse(res.body);
    expect(data.voted).toBe(true);
    expect(data.voteCount).toBe(3);
    expect(voteRepoMock.create).toHaveBeenCalled();
    expect(itemRepoMock.updateVoteCount).toHaveBeenCalledWith('item_1', 3);
  });

  it('POST /items/:itemId/vote removes vote when existing', async () => {
    itemRepoMock.getById.mockResolvedValue(baseItem);
    voteRepoMock.getByItemAndUser.mockResolvedValue({ id: 'vote_1' });
    voteRepoMock.countByItem.mockResolvedValue(2);

    const { voteRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(voteRoutes, { prefix: '/api' });

    const res = await app.inject({ method: 'POST', url: '/api/items/item_1/vote' });

    expect(res.statusCode).toBe(200);
    const data = JSON.parse(res.body);
    expect(data.voted).toBe(false);
    expect(data.voteCount).toBe(2);
    expect(voteRepoMock.remove).toHaveBeenCalledWith('vote_1');
  });

  it('POST /items/:itemId/vote returns 404 when item missing', async () => {
    itemRepoMock.getById.mockResolvedValue(null);

    const { voteRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(voteRoutes, { prefix: '/api' });

    const res = await app.inject({ method: 'POST', url: '/api/items/missing/vote' });

    expect(res.statusCode).toBe(404);
  });

  it('GET /items/:itemId/votes lists voters', async () => {
    itemRepoMock.getById.mockResolvedValue(baseItem);
    voteRepoMock.listByItem.mockResolvedValue([{ id: 'vote_1' }, { id: 'vote_2' }]);

    const { voteRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(voteRoutes, { prefix: '/api' });

    const res = await app.inject({ method: 'GET', url: '/api/items/item_1/votes' });

    expect(res.statusCode).toBe(200);
    const data = JSON.parse(res.body);
    expect(data.votes).toHaveLength(2);
    expect(data.count).toBe(2);
  });

  it('GET /items/:itemId/votes returns 404 when item missing', async () => {
    itemRepoMock.getById.mockResolvedValue(null);

    const { voteRoutes } = await import('./routes.js');
    const app = Fastify({ logger: false });
    await app.register(voteRoutes, { prefix: '/api' });

    const res = await app.inject({ method: 'GET', url: '/api/items/missing/votes' });

    expect(res.statusCode).toBe(404);
  });
});
