// src/utils/unitConverter.test.ts
import { describe, it, expect } from 'vitest';
import { formatUnitPrice, convertToMetric } from './unitConverter';
import type { UnitPrice } from '../types';

describe('formatUnitPrice', () => {
  it('should return a placeholder for null or invalid input', () => {
    expect(formatUnitPrice(null, 'metric')).toEqual({ price: '—', unit: null });
    expect(formatUnitPrice(undefined, 'imperial')).toEqual({ price: '—', unit: null });
    const invalidInput = { value: 'not-a-number' } as unknown as UnitPrice;
    expect(formatUnitPrice(invalidInput, 'metric')).toEqual({ price: '—', unit: null });
  });

  it('should handle a value of zero correctly', () => {
    const unitPrice: UnitPrice = { value: 0, unit: 'kg' };
    expect(formatUnitPrice(unitPrice, 'metric')).toEqual({ price: '$0.000', unit: '/kg' });
  });

  // --- No Conversion Tests ---
  it('should format a metric price correctly when the system is metric', () => {
    const unitPrice: UnitPrice = { value: 220, unit: 'kg' }; // $2.20/kg
    expect(formatUnitPrice(unitPrice, 'metric')).toEqual({ price: '$2.20', unit: '/kg' });
  });

  it('should format an imperial price correctly when the system is imperial', () => {
    const unitPrice: UnitPrice = { value: 100, unit: 'lb' }; // $1.00/lb
    expect(formatUnitPrice(unitPrice, 'imperial')).toEqual({ price: '$1.00', unit: '/lb' });
  });

  it('should format an "each" price correctly regardless of system', () => {
    const unitPrice: UnitPrice = { value: 500, unit: 'each' }; // $5.00/each
    expect(formatUnitPrice(unitPrice, 'metric')).toEqual({ price: '$5.00', unit: '/each' });
    expect(formatUnitPrice(unitPrice, 'imperial')).toEqual({ price: '$5.00', unit: '/each' });
  });

  // --- Conversion Tests ---
  it('should convert a metric price (kg) to imperial (lb)', () => {
    const unitPrice: UnitPrice = { value: 220, unit: 'kg' }; // $2.20/kg
    // 2.20 / 2.20462 = 0.9979... -> $1.00/lb
    expect(formatUnitPrice(unitPrice, 'imperial')).toEqual({ price: '$1.00', unit: '/lb' });
  });

  it('should convert an imperial price (lb) to metric (kg)', () => {
    const unitPrice: UnitPrice = { value: 100, unit: 'lb' }; // $1.00/lb
    // 1.00 / 0.453592 = 2.2046... -> $2.20/kg
    expect(formatUnitPrice(unitPrice, 'metric')).toEqual({ price: '$2.20', unit: '/kg' });
  });

  it('should convert a metric price (g) to imperial (oz)', () => {
    const unitPrice: UnitPrice = { value: 10, unit: 'g' }; // $0.10/g
    // 0.10 / 0.035274 = 2.83... -> $2.83/oz
    expect(formatUnitPrice(unitPrice, 'imperial')).toEqual({ price: '$2.83', unit: '/oz' });
  });

  it('should convert an imperial price (oz) to metric (g)', () => {
    const unitPrice: UnitPrice = { value: 50, unit: 'oz' }; // $0.50/oz
    // 0.50 / 28.3495 = 0.0176... -> $0.018/g
    expect(formatUnitPrice(unitPrice, 'metric')).toEqual({ price: '$0.018', unit: '/g' });
  });

  it('should convert a metric price (l) to imperial (fl oz)', () => {
    const unitPrice: UnitPrice = { value: 100, unit: 'l' }; // $1.00/l
    // 1.00 / 33.814 = 0.0295... -> $0.030/fl oz
    expect(formatUnitPrice(unitPrice, 'imperial')).toEqual({ price: '$0.030', unit: '/fl oz' });
  });

  it('should convert a metric price (ml) to imperial (fl oz)', () => {
    const unitPrice: UnitPrice = { value: 1, unit: 'ml' }; // $0.01/ml
    // 0.01 / 0.033814 = 0.2957... -> $0.30/fl oz
    expect(formatUnitPrice(unitPrice, 'imperial')).toEqual({ price: '$0.30', unit: '/fl oz' });
  });

  // --- Formatting Tests ---
  it('should use 3 decimal places for prices less than 10 cents', () => {
    const unitPrice: UnitPrice = { value: 5, unit: 'g' }; // $0.05/g
    // 0.05 / 28.3495 = 0.00176... -> $0.002/g
    expect(formatUnitPrice(unitPrice, 'metric')).toEqual({ price: '$0.050', unit: '/g' });
  });

  it('should use 2 decimal places for prices 10 cents or more', () => {
    const unitPrice: UnitPrice = { value: 15, unit: 'g' }; // $0.15/g
    expect(formatUnitPrice(unitPrice, 'metric')).toEqual({ price: '$0.15', unit: '/g' });
  });
});

describe('convertToMetric', () => {
  it('should return null or undefined if input is null or undefined', () => {
    expect(convertToMetric(null)).toBeNull();
    expect(convertToMetric(undefined)).toBeUndefined();
  });

  it('should not convert an already metric unit', () => {
    const metricPrice: UnitPrice = { value: 199, unit: 'kg' };
    expect(convertToMetric(metricPrice)).toEqual(metricPrice);
  });

  it('should not convert a non-standard unit like "each"', () => {
    const eachPrice: UnitPrice = { value: 500, unit: 'each' };
    expect(convertToMetric(eachPrice)).toEqual(eachPrice);
  });

  it('should convert from lb to kg', () => {
    const imperialPrice: UnitPrice = { value: 100, unit: 'lb' }; // $1.00/lb
    const expectedValue = 45.3592;
    expect(convertToMetric(imperialPrice)).toEqual({
      value: expectedValue,
      unit: 'kg',
    });
  });

  it('should convert from oz to g', () => {
    const imperialPrice: UnitPrice = { value: 10, unit: 'oz' }; // $0.10/oz
    const expectedValue = 283.495;
    expect(convertToMetric(imperialPrice)).toEqual({
      value: expectedValue,
      unit: 'g',
    });
  });

  it('should convert from fl oz to ml', () => {
    const imperialPrice: UnitPrice = { value: 5, unit: 'fl oz' }; // $0.05/fl oz
    const expectedValue = 147.8675;
    expect(convertToMetric(imperialPrice)).toEqual({
      value: expectedValue,
      unit: 'ml',
    });
  });

  it('should handle floating point inaccuracies during conversion', () => {
    // A value that might produce a long floating point number when converted
    const imperialPrice: UnitPrice = { value: 1, unit: 'oz' }; // $0.01/oz
    const result = convertToMetric(imperialPrice);
    expect(result?.value).toBeCloseTo(28.3495);
  });
});