// src/routes/personalization.routes.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import supertest from 'supertest';
import {
  createMockMasterGroceryItem,
  createMockDietaryRestriction,
  createMockAppliance,
} from '../tests/utils/mockFactories';
import { createTestApp } from '../tests/utils/createTestApp';

// 1. Mock the Service Layer directly.
vi.mock('../services/db/index.db', () => ({
  personalizationRepo: {
    getAllMasterItems: vi.fn(),
    getDietaryRestrictions: vi.fn(),
    getAppliances: vi.fn(),
  },
}));

// Import the router and mocked DB AFTER all mocks are defined.
import personalizationRouter from './personalization.routes';
import * as db from '../services/db/index.db';
import { mockLogger } from '../tests/utils/mockLogger';

// 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,
}));

describe('Personalization Routes (/api/personalization)', () => {
  const app = createTestApp({ router: personalizationRouter, basePath: '/api/personalization' });

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

  describe('GET /master-items', () => {
    it('should return a list of master items', async () => {
      const mockItems = [createMockMasterGroceryItem({ master_grocery_item_id: 1, name: 'Milk' })];
      vi.mocked(db.personalizationRepo.getAllMasterItems).mockResolvedValue({
        items: mockItems,
        total: 1,
      });

      const response = await supertest(app)
        .get('/api/personalization/master-items')
        .set('x-test-rate-limit-enable', 'true');

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual({ items: mockItems, total: 1 });
    });

    it('should return 500 if the database call fails', async () => {
      const dbError = new Error('DB Error');
      vi.mocked(db.personalizationRepo.getAllMasterItems).mockRejectedValue(dbError);
      const response = await supertest(app)
        .get('/api/personalization/master-items')
        .set('x-test-rate-limit-enable', 'true');
      expect(response.status).toBe(500);
      expect(response.body.error.message).toBe('DB Error');
      expect(mockLogger.error).toHaveBeenCalledWith(
        { error: dbError },
        'Error fetching master items in /api/personalization/master-items:',
      );
    });
  });

  describe('GET /dietary-restrictions', () => {
    it('should return a list of all dietary restrictions', async () => {
      const mockRestrictions = [createMockDietaryRestriction({ name: 'Gluten-Free' })];
      vi.mocked(db.personalizationRepo.getDietaryRestrictions).mockResolvedValue(mockRestrictions);

      const response = await supertest(app).get('/api/personalization/dietary-restrictions');

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual(mockRestrictions);
    });

    it('should return 500 if the database call fails', async () => {
      const dbError = new Error('DB Error');
      vi.mocked(db.personalizationRepo.getDietaryRestrictions).mockRejectedValue(dbError);
      const response = await supertest(app).get('/api/personalization/dietary-restrictions');
      expect(response.status).toBe(500);
      expect(response.body.error.message).toBe('DB Error');
      expect(mockLogger.error).toHaveBeenCalledWith(
        { error: dbError },
        'Error fetching dietary restrictions in /api/personalization/dietary-restrictions:',
      );
    });
  });

  describe('GET /appliances', () => {
    it('should return a list of all appliances', async () => {
      const mockAppliances = [createMockAppliance({ name: 'Air Fryer' })];
      vi.mocked(db.personalizationRepo.getAppliances).mockResolvedValue(mockAppliances);

      const response = await supertest(app).get('/api/personalization/appliances');

      expect(response.status).toBe(200);
      expect(response.body.data).toEqual(mockAppliances);
    });

    it('should return 500 if the database call fails', async () => {
      const dbError = new Error('DB Error');
      vi.mocked(db.personalizationRepo.getAppliances).mockRejectedValue(dbError);
      const response = await supertest(app).get('/api/personalization/appliances');
      expect(response.status).toBe(500);
      expect(response.body.error.message).toBe('DB Error');
      expect(mockLogger.error).toHaveBeenCalledWith(
        { error: dbError },
        'Error fetching appliances in /api/personalization/appliances:',
      );
    });
  });

  describe('Rate Limiting', () => {
    it('should apply publicReadLimiter to GET /master-items', async () => {
      vi.mocked(db.personalizationRepo.getAllMasterItems).mockResolvedValue({
        items: [],
        total: 0,
      });
      const response = await supertest(app)
        .get('/api/personalization/master-items')
        .set('X-Test-Rate-Limit-Enable', 'true');

      expect(response.status).toBe(200);
      expect(response.headers).toHaveProperty('ratelimit-limit');
    });
  });
});
