// src/routes/price.routes.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import supertest from 'supertest';
import { createTestApp } from '../tests/utils/createTestApp';
import { mockLogger } from '../tests/utils/mockLogger';

// Mock the price repository
vi.mock('../services/db/price.db', () => ({
  priceRepo: {
    getPriceHistory: vi.fn(),
  },
}));

// Mock the logger to keep test output clean
vi.mock('../services/logger.server', async () => ({
  // Use async import to avoid hoisting issues with mockLogger
  logger: (await import('../tests/utils/mockLogger')).mockLogger,
}));

// Import the router AFTER other setup.
import priceRouter from './price.routes';
import { priceRepo } from '../services/db/price.db';

describe('Price Routes (/api/price-history)', () => {
  const app = createTestApp({ router: priceRouter, basePath: '/api/price-history' });
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('POST /', () => {
    it('should return 200 OK with price history data for a valid request', async () => {
      const mockHistory = [
        { master_item_id: 1, price_in_cents: 199, date: '2024-01-01T00:00:00.000Z' },
        { master_item_id: 2, price_in_cents: 299, date: '2024-01-08T00:00:00.000Z' },
      ];
      vi.mocked(priceRepo.getPriceHistory).mockResolvedValue(mockHistory);

      const response = await supertest(app)
        .post('/api/price-history')
        .send({ masterItemIds: [1, 2] });

      expect(response.status).toBe(200);
      expect(response.body).toEqual(mockHistory);
      expect(priceRepo.getPriceHistory).toHaveBeenCalledWith([1, 2], expect.any(Object), 1000, 0);
    });

    it('should pass limit and offset from the body to the repository', async () => {
      vi.mocked(priceRepo.getPriceHistory).mockResolvedValue([]);
      await supertest(app)
        .post('/api/price-history')
        .send({ masterItemIds: [1, 2, 3], limit: 50, offset: 10 });

      expect(priceRepo.getPriceHistory).toHaveBeenCalledWith(
        [1, 2, 3],
        expect.any(Object),
        50,
        10,
      );
    });

    it('should log the request info', async () => {
      vi.mocked(priceRepo.getPriceHistory).mockResolvedValue([]);
      await supertest(app)
        .post('/api/price-history')
        .send({ masterItemIds: [1, 2, 3], limit: 25, offset: 5 });

      expect(mockLogger.info).toHaveBeenCalledWith(
        { itemCount: 3, limit: 25, offset: 5 },
        '[API /price-history] Received request for historical price data.',
      );
    });

    it('should return 500 if the database call fails', async () => {
      const dbError = new Error('Database connection failed');
      vi.mocked(priceRepo.getPriceHistory).mockRejectedValue(dbError);

      const response = await supertest(app)
        .post('/api/price-history')
        .send({ masterItemIds: [1, 2, 3] });

      expect(response.status).toBe(500);
      expect(response.body.message).toBe('Database connection failed');
    });

    it('should return 400 if masterItemIds is an empty array', async () => {
      const response = await supertest(app).post('/api/price-history').send({ masterItemIds: [] });

      expect(response.status).toBe(400);
      expect(response.body.errors[0].message).toBe(
        'masterItemIds must be a non-empty array of positive integers.',
      );
    });

    it('should return 400 if masterItemIds is not an array', async () => {
      const response = await supertest(app)
        .post('/api/price-history')
        .send({ masterItemIds: 'not-an-array' });

      expect(response.status).toBe(400);
      // The actual message is "Invalid input: expected array, received string"
      expect(response.body.errors[0].message).toBe('Invalid input: expected array, received string');
    });

    it('should return 400 if masterItemIds contains non-positive integers', async () => {
      const response = await supertest(app)
        .post('/api/price-history')
        .send({ masterItemIds: [1, -2, 3] });

      expect(response.status).toBe(400);
      expect(response.body.errors[0].message).toBe('Number must be greater than 0');
    });

    it('should return 400 if masterItemIds is missing', async () => {
      const response = await supertest(app).post('/api/price-history').send({});

      expect(response.status).toBe(400);
      // The actual message is "Invalid input: expected array, received undefined"
      expect(response.body.errors[0].message).toBe('Invalid input: expected array, received undefined');
    });

    it('should return 400 for invalid limit and offset', async () => {
      const response = await supertest(app)
        .post('/api/price-history')
        .send({ masterItemIds: [1], limit: -1, offset: 'abc' });

      expect(response.status).toBe(400);
      expect(response.body.errors).toHaveLength(2);
      // The actual message is "Too small: expected number to be >0"
      expect(response.body.errors[0].message).toBe('Too small: expected number to be >0');
      expect(response.body.errors[1].message).toBe('Invalid input: expected number, received NaN');
    });
  });
});
