"""Tests for app.utils.units."""
from decimal import Decimal

import pytest

from app.utils.units import UnitConverter, convert_qty, normalize_unit


class TestNormalizeUnit:
    def test_synonyms(self):
        assert normalize_unit("pounds") == "lb"
        assert normalize_unit("OUNCE") == "oz"
        assert normalize_unit("Cups") == "cup"
        assert normalize_unit("TBS") == "tbsp"
        assert normalize_unit("each") == "ea"
        assert normalize_unit("doz") == "dozen"

    def test_already_canonical(self):
        assert normalize_unit("lb") == "lb"
        assert normalize_unit("cup") == "cup"
        assert normalize_unit("ea") == "ea"

    def test_unknown_passthrough(self):
        assert normalize_unit("bunch") == "bunch"
        assert normalize_unit("can") == "can"

    def test_none(self):
        assert normalize_unit(None) == "unknown"
        assert normalize_unit("") == "unknown"


class TestWithinFamilyConversion:
    def test_weight_lb_to_oz(self):
        assert convert_qty(2.0, "lb", "oz") == Decimal("32")

    def test_weight_oz_to_lb(self):
        result = convert_qty(16.0, "oz", "lb")
        assert result == Decimal("1.0000")

    def test_volume_cup_to_tbsp(self):
        result = convert_qty(1.0, "cup", "tbsp")
        assert result == Decimal("16")

    def test_volume_tbsp_to_tsp(self):
        result = convert_qty(1.0, "tbsp", "tsp")
        assert result == Decimal("3")

    def test_count_dozen_to_ea(self):
        result = convert_qty(1.0, "dozen", "ea")
        assert result == Decimal("12")

    def test_same_unit(self):
        assert convert_qty(5.0, "lb", "lb") == Decimal("5")


class TestDensityConversion:
    def test_olive_oil_tbsp_to_cup(self):
        # 1 cup = 16 tbsp
        result = convert_qty(
            1.0, "cup", "tbsp", ingredient_name_lower="olive oil"
        )
        assert result == Decimal("16")

    def test_rice_cup_to_lb(self):
        # 1 cup rice = 185 g; 1 lb = 453.592 g → 1 cup = 185/453.592 ≈ 0.4079 lb
        result = convert_qty(
            1.0, "cup", "lb", ingredient_name_lower="rice, long-grain white"
        )
        assert result == Decimal("0.4079")

    def test_butter_tbsp_to_cup(self):
        result = convert_qty(
            1.0, "cup", "tbsp", ingredient_name_lower="butter, unsalted"
        )
        assert result == Decimal("16")

    def test_seed_size_ea_to_lb(self):
        # 1 avocado ≈ 170 g = 0.3748 lb
        result = convert_qty(
            1.0, "ea", "lb", ingredient_name_lower="avocado"
        )
        assert result == Decimal("0.3748")

    def test_unknown_density_falls_back(self):
        # No density for "dragon fruit" → fallback to dimensionless qty
        result = convert_qty(2.0, "cup", "lb", ingredient_name_lower="dragon fruit")
        assert result == Decimal("2")


class TestFallback:
    def test_incommensurable_units_without_name(self):
        result = convert_qty(2.0, "cup", "lb")
        assert result == Decimal("2")

    def test_incommensurable_units_with_unmapped_name(self):
        result = convert_qty(2.0, "cup", "lb", ingredient_name_lower="xyz")
        assert result == Decimal("2")

    def test_missing_from_unit(self):
        result = convert_qty(2.0, None, "lb")
        assert result == Decimal("2")

    def test_missing_to_unit(self):
        result = convert_qty(2.0, "lb", None)
        assert result == Decimal("2")
