"""Entropyk — Unit Tests for Core Physical Types.

Tests for Pressure, Temperature, Enthalpy, and MassFlow wrappers.
"""

import pytest
import entropyk


class TestPressure:
    """Tests for Pressure type."""

    def test_from_pa(self):
        p = entropyk.Pressure(pa=101325.0)
        assert p.to_pascals() == pytest.approx(101325.0)

    def test_from_bar(self):
        p = entropyk.Pressure(bar=1.01325)
        assert p.to_pascals() == pytest.approx(101325.0)

    def test_from_kpa(self):
        p = entropyk.Pressure(kpa=101.325)
        assert p.to_pascals() == pytest.approx(101325.0)

    def test_from_psi(self):
        p = entropyk.Pressure(psi=14.696)
        assert p.to_pascals() == pytest.approx(101325.0, rel=1e-3)

    def test_to_bar(self):
        p = entropyk.Pressure(pa=100000.0)
        assert p.to_bar() == pytest.approx(1.0)

    def test_to_kpa(self):
        p = entropyk.Pressure(pa=1000.0)
        assert p.to_kpa() == pytest.approx(1.0)

    def test_float(self):
        p = entropyk.Pressure(pa=101325.0)
        assert float(p) == pytest.approx(101325.0)

    def test_repr(self):
        p = entropyk.Pressure(bar=1.0)
        assert "Pressure" in repr(p)
        assert "bar" in repr(p)

    def test_str(self):
        p = entropyk.Pressure(pa=100.0)
        assert "Pa" in str(p)

    def test_eq(self):
        p1 = entropyk.Pressure(bar=1.0)
        p2 = entropyk.Pressure(bar=1.0)
        assert p1 == p2

    def test_add(self):
        p1 = entropyk.Pressure(pa=100.0)
        p2 = entropyk.Pressure(pa=200.0)
        result = p1 + p2
        assert float(result) == pytest.approx(300.0)

    def test_sub(self):
        p1 = entropyk.Pressure(pa=300.0)
        p2 = entropyk.Pressure(pa=100.0)
        result = p1 - p2
        assert float(result) == pytest.approx(200.0)

    def test_multiple_kwargs_raises(self):
        with pytest.raises(ValueError, match="exactly one"):
            entropyk.Pressure(pa=100.0, bar=1.0)

    def test_no_kwargs_raises(self):
        with pytest.raises(ValueError, match="exactly one"):
            entropyk.Pressure()


class TestTemperature:
    """Tests for Temperature type."""

    def test_from_kelvin(self):
        t = entropyk.Temperature(kelvin=300.0)
        assert t.to_kelvin() == pytest.approx(300.0)

    def test_from_celsius(self):
        t = entropyk.Temperature(celsius=25.0)
        assert t.to_kelvin() == pytest.approx(298.15)

    def test_from_fahrenheit(self):
        t = entropyk.Temperature(fahrenheit=77.0)
        assert t.to_celsius() == pytest.approx(25.0)

    def test_to_celsius(self):
        t = entropyk.Temperature(kelvin=273.15)
        assert t.to_celsius() == pytest.approx(0.0)

    def test_to_fahrenheit(self):
        t = entropyk.Temperature(celsius=100.0)
        assert t.to_fahrenheit() == pytest.approx(212.0)

    def test_float(self):
        t = entropyk.Temperature(kelvin=300.0)
        assert float(t) == pytest.approx(300.0)

    def test_repr(self):
        t = entropyk.Temperature(celsius=25.0)
        assert "Temperature" in repr(t)

    def test_eq(self):
        t1 = entropyk.Temperature(celsius=25.0)
        t2 = entropyk.Temperature(celsius=25.0)
        assert t1 == t2

    def test_add(self):
        t1 = entropyk.Temperature(kelvin=100.0)
        t2 = entropyk.Temperature(kelvin=200.0)
        result = t1 + t2
        assert float(result) == pytest.approx(300.0)

    def test_sub(self):
        t1 = entropyk.Temperature(kelvin=300.0)
        t2 = entropyk.Temperature(kelvin=100.0)
        result = t1 - t2
        assert float(result) == pytest.approx(200.0)

    def test_multiple_kwargs_raises(self):
        with pytest.raises(ValueError, match="exactly one"):
            entropyk.Temperature(kelvin=300.0, celsius=25.0)


class TestEnthalpy:
    """Tests for Enthalpy type."""

    def test_from_j_per_kg(self):
        h = entropyk.Enthalpy(j_per_kg=250000.0)
        assert h.to_j_per_kg() == pytest.approx(250000.0)

    def test_from_kj_per_kg(self):
        h = entropyk.Enthalpy(kj_per_kg=250.0)
        assert h.to_j_per_kg() == pytest.approx(250000.0)

    def test_to_kj_per_kg(self):
        h = entropyk.Enthalpy(j_per_kg=250000.0)
        assert h.to_kj_per_kg() == pytest.approx(250.0)

    def test_float(self):
        h = entropyk.Enthalpy(j_per_kg=250000.0)
        assert float(h) == pytest.approx(250000.0)

    def test_repr(self):
        h = entropyk.Enthalpy(kj_per_kg=250.0)
        assert "Enthalpy" in repr(h)

    def test_eq(self):
        h1 = entropyk.Enthalpy(kj_per_kg=250.0)
        h2 = entropyk.Enthalpy(kj_per_kg=250.0)
        assert h1 == h2

    def test_add(self):
        h1 = entropyk.Enthalpy(j_per_kg=100.0)
        h2 = entropyk.Enthalpy(j_per_kg=200.0)
        result = h1 + h2
        assert float(result) == pytest.approx(300.0)

    def test_sub(self):
        h1 = entropyk.Enthalpy(j_per_kg=300.0)
        h2 = entropyk.Enthalpy(j_per_kg=100.0)
        result = h1 - h2
        assert float(result) == pytest.approx(200.0)


class TestMassFlow:
    """Tests for MassFlow type."""

    def test_from_kg_per_s(self):
        m = entropyk.MassFlow(kg_per_s=0.5)
        assert m.to_kg_per_s() == pytest.approx(0.5)

    def test_from_g_per_s(self):
        m = entropyk.MassFlow(g_per_s=500.0)
        assert m.to_kg_per_s() == pytest.approx(0.5)

    def test_to_g_per_s(self):
        m = entropyk.MassFlow(kg_per_s=0.5)
        assert m.to_g_per_s() == pytest.approx(500.0)

    def test_float(self):
        m = entropyk.MassFlow(kg_per_s=0.5)
        assert float(m) == pytest.approx(0.5)

    def test_repr(self):
        m = entropyk.MassFlow(kg_per_s=0.5)
        assert "MassFlow" in repr(m)

    def test_eq(self):
        m1 = entropyk.MassFlow(kg_per_s=0.5)
        m2 = entropyk.MassFlow(kg_per_s=0.5)
        assert m1 == m2

    def test_add(self):
        m1 = entropyk.MassFlow(kg_per_s=0.1)
        m2 = entropyk.MassFlow(kg_per_s=0.2)
        result = m1 + m2
        assert float(result) == pytest.approx(0.3)

    def test_sub(self):
        m1 = entropyk.MassFlow(kg_per_s=0.5)
        m2 = entropyk.MassFlow(kg_per_s=0.2)
        result = m1 - m2
        assert float(result) == pytest.approx(0.3)
