import { describe, it, expect } from 'vitest';
import { toMetric } from '../../../src/modules/recipes/unit-conversion.service.js';

describe('toMetric', () => {
  describe('metric pass-through', () => {
    it('passes g through unchanged', () => {
      const r = toMetric(100, 'g', 'g');
      expect(r).toEqual({ ok: true, quantity: 100, unit: 'g' });
    });

    it('passes ml through unchanged', () => {
      const r = toMetric(250, 'ml', 'ml');
      expect(r).toEqual({ ok: true, quantity: 250, unit: 'ml' });
    });

    it('passes piece through unchanged', () => {
      const r = toMetric(2, 'piece', 'piece');
      expect(r).toEqual({ ok: true, quantity: 2, unit: 'piece' });
    });

    it('passes slice through unchanged', () => {
      const r = toMetric(3, 'slice', 'slice');
      expect(r).toEqual({ ok: true, quantity: 3, unit: 'slice' });
    });
  });

  describe('mass conversions', () => {
    it('converts oz to g for a g-product', () => {
      const r = toMetric(1, 'oz', 'g');
      expect(r).toEqual({ ok: true, quantity: 28.35, unit: 'g' });
    });

    it('converts lb to g for a g-product', () => {
      const r = toMetric(1, 'lb', 'g');
      expect(r).toEqual({ ok: true, quantity: 453.592, unit: 'g' });
    });

    it('converts oz to ml using density for a ml-product', () => {
      // 1 oz = 28.3495 g; density 1.03 g/ml → 27.524... ml
      const r = toMetric(1, 'oz', 'ml', 1.03);
      expect(r.ok).toBe(true);
      if (r.ok) {
        expect(r.unit).toBe('ml');
        expect(r.quantity).toBeCloseTo(27.524, 2);
      }
    });

    it('returns MISSING_DENSITY for oz → ml when density absent', () => {
      const r = toMetric(1, 'oz', 'ml');
      expect(r).toMatchObject({ ok: false, code: 'MISSING_DENSITY' });
    });

    it('returns INCOMPATIBLE_UNITS for oz → piece', () => {
      const r = toMetric(1, 'oz', 'piece');
      expect(r).toMatchObject({ ok: false, code: 'INCOMPATIBLE_UNITS' });
    });
  });

  describe('volume conversions', () => {
    it('converts tsp to ml for a ml-product', () => {
      const r = toMetric(1, 'tsp', 'ml');
      expect(r).toEqual({ ok: true, quantity: 4.929, unit: 'ml' });
    });

    it('converts tbsp to ml for a ml-product', () => {
      const r = toMetric(1, 'tbsp', 'ml');
      expect(r).toEqual({ ok: true, quantity: 14.787, unit: 'ml' });
    });

    it('converts fl_oz to ml for a ml-product', () => {
      const r = toMetric(1, 'fl_oz', 'ml');
      expect(r).toEqual({ ok: true, quantity: 29.574, unit: 'ml' });
    });

    it('converts cup to ml for a ml-product', () => {
      const r = toMetric(1, 'cup', 'ml');
      expect(r).toEqual({ ok: true, quantity: 236.588, unit: 'ml' });
    });

    it('converts cup to g using density for a g-product', () => {
      // 1 cup = 236.588 ml; density 1.05 g/ml → 248.417 g
      const r = toMetric(1, 'cup', 'g', 1.05);
      expect(r.ok).toBe(true);
      if (r.ok) {
        expect(r.unit).toBe('g');
        expect(r.quantity).toBeCloseTo(248.417, 2);
      }
    });

    it('returns MISSING_DENSITY for cup → g when density absent', () => {
      const r = toMetric(1, 'cup', 'g');
      expect(r).toMatchObject({ ok: false, code: 'MISSING_DENSITY' });
    });

    it('returns INCOMPATIBLE_UNITS for cup → piece', () => {
      const r = toMetric(1, 'cup', 'piece');
      expect(r).toMatchObject({ ok: false, code: 'INCOMPATIBLE_UNITS' });
    });
  });

  describe('mass to ml cross-conversion', () => {
    it('converts oz to ml using density', () => {
      // 1 oz = 28.3495 g; density 0.9 g/ml → 28.3495 / 0.9 = 31.499... ml
      const r = toMetric(1, 'oz', 'ml', 0.9);
      expect(r.ok).toBe(true);
      if (r.ok) {
        expect(r.unit).toBe('ml');
        expect(r.quantity).toBeCloseTo(31.499, 1);
      }
    });

    it('returns MISSING_DENSITY for oz → ml when density absent', () => {
      const r = toMetric(1, 'oz', 'ml');
      expect(r).toMatchObject({ ok: false, code: 'MISSING_DENSITY' });
    });

    it('returns INCOMPATIBLE_UNITS for oz → piece', () => {
      const r = toMetric(1, 'oz', 'piece');
      expect(r).toMatchObject({ ok: false, code: 'INCOMPATIBLE_UNITS' });
    });
  });

  describe('unknown unit', () => {
    it('returns INCOMPATIBLE_UNITS for unknown unit', () => {
      const r = toMetric(1, 'gallon' as never, 'g');
      expect(r).toMatchObject({ ok: false, code: 'INCOMPATIBLE_UNITS' });
      if (!r.ok) {
        expect(r.message).toContain('Unknown unit');
      }
    });
  });
});
