// src/services/db/conversion.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mockPoolInstance } from '../../tests/setup/tests-setup-unit';
import { getPool } from './connection.db';
import { conversionRepo } from './conversion.db';
import { NotFoundError } from './errors.db';
import type { UnitConversion } from '../../types';

// Un-mock the module we are testing
vi.unmock('./conversion.db');

// Mock dependencies
vi.mock('./connection.db', () => ({
  getPool: vi.fn(),
}));

vi.mock('../logger.server', () => ({
  logger: {
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
    debug: vi.fn(),
  },
}));

import { logger as mockLogger } from '../logger.server';

describe('Conversion DB Service', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockPoolInstance.query.mockReset();
    // Make getPool return our mock instance for each test
    vi.mocked(getPool).mockReturnValue(mockPoolInstance as any);
  });

  describe('getConversions', () => {
    it('should return all conversions if no filters are provided', async () => {
      const mockConversions: UnitConversion[] = [
        {
          unit_conversion_id: 1,
          master_item_id: 1,
          from_unit: 'g',
          to_unit: 'kg',
          factor: 0.001,
          created_at: new Date().toISOString(),
          updated_at: new Date().toISOString(),
        },
      ];
      mockPoolInstance.query.mockResolvedValue({ rows: mockConversions });

      const result = await conversionRepo.getConversions({}, mockLogger);

      expect(mockPoolInstance.query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM public.unit_conversions'),
        expect.any(Array),
      );
      // Check that WHERE clause is not present for master_item_id
      expect(mockPoolInstance.query.mock.calls[0][0]).not.toContain('WHERE master_item_id');
      expect(result).toEqual(mockConversions);
    });

    it('should filter by masterItemId', async () => {
      mockPoolInstance.query.mockResolvedValue({ rows: [] });

      await conversionRepo.getConversions({ masterItemId: 123 }, mockLogger);

      expect(mockPoolInstance.query).toHaveBeenCalledWith(
        expect.stringContaining('WHERE master_item_id = $1'),
        [123],
      );
    });

    it('should throw an error if the database query fails', async () => {
      const dbError = new Error('DB Error');
      mockPoolInstance.query.mockRejectedValue(dbError);

      await expect(conversionRepo.getConversions({}, mockLogger)).rejects.toThrow(
        'Failed to retrieve unit conversions.',
      );
      expect(mockLogger.error).toHaveBeenCalledWith(
        { err: dbError, filters: {} },
        'Database error in getConversions',
      );
    });
  });

  describe('createConversion', () => {
    const newConversion = {
      master_item_id: 1,
      from_unit: 'cup',
      to_unit: 'ml',
      factor: 236.588,
    };

    it('should insert a new conversion and return it', async () => {
      const mockCreatedConversion: UnitConversion = {
        unit_conversion_id: 1,
        ...newConversion,
        created_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
      };
      mockPoolInstance.query.mockResolvedValue({ rows: [mockCreatedConversion] });

      const result = await conversionRepo.createConversion(newConversion, mockLogger);

      expect(mockPoolInstance.query).toHaveBeenCalledWith(
        expect.stringContaining('INSERT INTO public.unit_conversions'),
        [1, 'cup', 'ml', 236.588],
      );
      expect(result).toEqual(mockCreatedConversion);
    });

    it('should throw an error if the database query fails', async () => {
      const dbError = new Error('DB Error');
      mockPoolInstance.query.mockRejectedValue(dbError);

      await expect(conversionRepo.createConversion(newConversion, mockLogger)).rejects.toThrow(
        'Failed to create unit conversion.',
      );
      expect(mockLogger.error).toHaveBeenCalledWith(
        { err: dbError, conversionData: newConversion },
        'Database error in createConversion',
      );
    });
  });

  describe('deleteConversion', () => {
    it('should delete a conversion if found', async () => {
      mockPoolInstance.query.mockResolvedValue({ rowCount: 1 });

      await conversionRepo.deleteConversion(1, mockLogger);

      expect(mockPoolInstance.query).toHaveBeenCalledWith(
        'DELETE FROM public.unit_conversions WHERE unit_conversion_id = $1',
        [1],
      );
    });

    it('should throw NotFoundError if conversion is not found', async () => {
      mockPoolInstance.query.mockResolvedValue({ rowCount: 0 });

      await expect(conversionRepo.deleteConversion(999, mockLogger)).rejects.toThrow(NotFoundError);
      await expect(conversionRepo.deleteConversion(999, mockLogger)).rejects.toThrow(
        'Unit conversion with ID 999 not found.',
      );
    });

    it('should throw an error if the database query fails', async () => {
      const dbError = new Error('DB Error');
      mockPoolInstance.query.mockRejectedValue(dbError);

      await expect(conversionRepo.deleteConversion(1, mockLogger)).rejects.toThrow(
        'Failed to delete unit conversion.',
      );
      expect(mockLogger.error).toHaveBeenCalledWith(
        { err: dbError, conversionId: 1 },
        'Database error in deleteConversion',
      );
    });
  });
});