// src/routes/deals.routes.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import supertest from 'supertest';
import { Request, Response, NextFunction } from 'express';
import dealsRouter from './deals.routes';
import { dealsRepo } from '../services/db/deals.db';
import { createMockUserProfile, createMockWatchedItemDeal } from '../tests/utils/mockFactories';
import type { WatchedItemDeal } from '../types';
import { mockLogger } from '../tests/utils/mockLogger';
import { createTestApp } from '../tests/utils/createTestApp';

// 1. Mock the Service Layer directly.
vi.mock('../services/db/deals.db', () => ({
  dealsRepo: {
    findBestPricesForWatchedItems: vi.fn(),
  },
}));

// Mock the logger to keep test output clean
vi.mock('../services/logger.server', () => ({
  logger: mockLogger,
}));

// Mock the passport middleware
vi.mock('./passport.routes', () => ({
  default: {
    authenticate: vi.fn((_strategy, _options) => (req: Request, res: Response, next: NextFunction) => {
      // If req.user is not set by the test setup, simulate unauthenticated access.
      if (!req.user) {
        return res.status(401).json({ message: 'Unauthorized' });
      }
      // If req.user is set, proceed as an authenticated user.
      next();
    }),
  },
}));

// Define a reusable matcher for the logger object.
const expectLogger = expect.objectContaining({
  info: expect.any(Function),
  error: expect.any(Function),
});

describe('Deals Routes (/api/users/deals)', () => {
  const mockUser = createMockUserProfile({ user_id: 'user-123' });
  const basePath = '/api/users/deals';
  const authenticatedApp = createTestApp({ router: dealsRouter, basePath, authenticatedUser: mockUser });
  const unauthenticatedApp = createTestApp({ router: dealsRouter, basePath });
  const errorHandler = (err: any, req: any, res: any, next: any) => {
    res.status(err.status || 500).json({ message: err.message, errors: err.errors });
  };

  // Apply the handler to both app instances
  authenticatedApp.use(errorHandler);
  unauthenticatedApp.use(errorHandler);

  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('GET /best-watched-prices', () => {
    it('should return 401 Unauthorized if user is not authenticated', async () => {
      const response = await supertest(unauthenticatedApp).get('/api/users/deals/best-watched-prices');
      expect(response.status).toBe(401);
    });

    it('should return a list of deals for an authenticated user', async () => {
      const mockDeals: WatchedItemDeal[] = [createMockWatchedItemDeal({ item_name: 'Apples' })];
      vi.mocked(dealsRepo.findBestPricesForWatchedItems).mockResolvedValue(mockDeals);

      const response = await supertest(authenticatedApp).get('/api/users/deals/best-watched-prices');

      expect(response.status).toBe(200);
      expect(response.body).toEqual(mockDeals);
      expect(dealsRepo.findBestPricesForWatchedItems).toHaveBeenCalledWith(mockUser.user_id, expectLogger);
    });

    it('should return 500 if the database call fails', async () => {
      vi.mocked(dealsRepo.findBestPricesForWatchedItems).mockRejectedValue(new Error('DB Error'));

      const response = await supertest(authenticatedApp).get('/api/users/deals/best-watched-prices');
      expect(response.status).toBe(500);
      expect(response.body.message).toBe('DB Error');
    });
  });
});