"""Tests for radiobiological tissue models."""

import numpy as np
import pytest

from oncosim.physics.tissue_models import (
    surviving_fraction,
    tcp,
    ntcp_lkb,
    bed,
    TUMOR_PARAMS,
    NORMAL_TISSUE_PARAMS,
)


class TestSurvivingFraction:
    def test_zero_dose(self):
        sf = surviving_fraction(0.0, 0.3, 0.03)
        assert sf == pytest.approx(1.0)

    def test_positive_dose_less_than_one(self):
        sf = surviving_fraction(2.0, 0.3, 0.03)
        assert 0 < sf < 1

    def test_high_dose_approaches_zero(self):
        sf = surviving_fraction(20.0, 0.3, 0.03)
        assert sf < 0.01

    def test_monotonic_decrease(self):
        doses = [0, 1, 2, 3, 4, 5]
        sfs = [surviving_fraction(d, 0.3, 0.03) for d in doses]
        for i in range(len(sfs) - 1):
            assert sfs[i] > sfs[i + 1]

    def test_alpha_beta_ratio_effect(self):
        # Higher alpha/beta = more sensitive to single dose
        sf_high = surviving_fraction(2.0, 0.3, 0.03)   # a/b = 10
        sf_low = surviving_fraction(2.0, 0.1, 0.033)    # a/b = 3
        # Both should be between 0 and 1
        assert 0 < sf_high < 1
        assert 0 < sf_low < 1

    def test_range_always_zero_to_one(self):
        for dose in [0.1, 0.5, 1, 2, 5, 10]:
            sf = surviving_fraction(dose, 0.3, 0.03)
            assert 0 <= sf <= 1


class TestTCP:
    def test_zero_dose_zero_tcp(self):
        t = tcp(0.0, 0, 0.3, 0.03)
        assert t < 0.01

    def test_high_dose_approaches_one(self):
        t = tcp(2.0, 30, 0.3, 0.03)
        assert t > 0.5

    def test_monotonic_with_fractions(self):
        tcps = [tcp(2.0, n, 0.3, 0.03) for n in [1, 5, 10, 20, 30]]
        for i in range(len(tcps) - 1):
            assert tcps[i] <= tcps[i + 1] + 1e-10

    def test_range_zero_to_one(self):
        for d in [0.5, 1.0, 2.0, 3.0]:
            for n in [1, 10, 30]:
                t = tcp(d, n, 0.3, 0.03)
                assert 0 <= t <= 1

    def test_higher_dose_higher_tcp(self):
        t_low = tcp(1.0, 30, 0.3, 0.03)
        t_high = tcp(3.0, 30, 0.3, 0.03)
        assert t_high >= t_low

    def test_with_default_params(self):
        t = tcp(2.0, 30)
        assert 0 <= t <= 1


class TestNTCP:
    def test_below_td50_low_ntcp(self):
        n = ntcp_lkb(20.0, td50=50.0)
        assert n < 0.1

    def test_above_td50_high_ntcp(self):
        n = ntcp_lkb(80.0, td50=50.0)
        assert n > 0.5

    def test_at_td50_approx_half(self):
        n = ntcp_lkb(50.0, td50=50.0)
        assert abs(n - 0.5) < 0.05

    def test_zero_dose_near_zero(self):
        n = ntcp_lkb(0.0, td50=50.0)
        assert n < 0.01

    def test_monotonic_increase(self):
        doses = [10, 20, 30, 40, 50, 60, 70, 80]
        ntcps = [ntcp_lkb(d, td50=50.0) for d in doses]
        for i in range(len(ntcps) - 1):
            assert ntcps[i] <= ntcps[i + 1] + 1e-10

    def test_range_zero_to_one(self):
        for d in [0, 20, 50, 80, 100]:
            n = ntcp_lkb(float(d), td50=50.0)
            assert 0 <= n <= 1

    def test_invalid_td50(self):
        n = ntcp_lkb(50.0, td50=0.0)
        assert n == 0.0


class TestBED:
    def test_standard_fractionation(self):
        # 60 Gy in 30 fractions of 2 Gy, alpha/beta=10
        b = bed(2.0, 30, alpha_beta=10.0)
        assert b == pytest.approx(72.0)

    def test_hypofractionation(self):
        # Higher dose per fraction should give higher BED
        b_standard = bed(2.0, 30, alpha_beta=10.0)
        b_hypo = bed(5.0, 12, alpha_beta=10.0)
        # 5*12=60 Gy physical, but higher BED
        assert b_hypo > b_standard

    def test_zero_dose(self):
        b = bed(0.0, 30, alpha_beta=10.0)
        assert b == 0.0

    def test_single_fraction(self):
        b = bed(2.0, 1, alpha_beta=10.0)
        assert b == pytest.approx(2.4)


class TestParameters:
    def test_tumor_params_exist(self):
        assert "alpha" in TUMOR_PARAMS
        assert "beta" in TUMOR_PARAMS
        assert "N0" in TUMOR_PARAMS

    def test_normal_tissue_params_exist(self):
        assert "late_responding" in NORMAL_TISSUE_PARAMS
        assert "early_responding" in NORMAL_TISSUE_PARAMS

    def test_tumor_alpha_beta_ratio(self):
        ratio = TUMOR_PARAMS["alpha"] / TUMOR_PARAMS["beta"]
        assert ratio == pytest.approx(10.0)
