import pytest
from mento.rebar import Rebar
from mento.beam import RectangularBeam
from mento.material import Concrete_ACI_318_19, SteelBar
from mento.units import psi, kip, mm, inch, ksi, cm, MPa, kN
from mento.forces import Forces
from mento.node import Node

@pytest.fixture()
def beam_example_imperial() -> RectangularBeam:
    concrete = Concrete_ACI_318_19(name="C4", f_c=4000*psi)  
    steelBar = SteelBar(name="ADN 420", f_y=60*ksi) 
    custom_settings = {'clear_cover': 1.5*inch, 'stirrup_diameter_ini':0.5*inch,
                       'longitudinal_diameter_ini': 1*inch} 
    section = RectangularBeam(
        label="V-10x16",
        concrete=concrete,
        steel_bar=steelBar,
        width=10*inch,  
        height=16*inch,
        settings=custom_settings  
    )
    return section

@pytest.fixture()
def beam_example_metric() -> RectangularBeam:
    concrete= Concrete_ACI_318_19(name="H30",f_c=30*MPa) 
    steelBar= SteelBar(name="ADN 420", f_y=420*MPa)
    custom_settings = {'clear_cover': 30*mm, 'stirrup_diameter_ini':8*mm}
    section = RectangularBeam(
        label="V 20x50",
        concrete=concrete,
        steel_bar=steelBar,
        width=20*cm,  
        height=50*cm,
        settings=custom_settings  
    )
    return section

# Tests
def test_beam_longitudinal_rebar_ACI_318_19_metric(beam_example_metric: RectangularBeam) -> None:
    A_s_req = 5 * cm**2
    beam_rebar = Rebar(beam_example_metric)
    beam_rebar.longitudinal_rebar_ACI_318_19(A_s_req=A_s_req)
    best_design = beam_rebar.longitudinal_rebar_design

    assert best_design is not None
    assert best_design['n_1'] == 2
    assert best_design['d_b1'] == 16 * mm
    assert best_design['n_2'] == 1
    assert best_design['d_b2'] == 12 *mm
    assert best_design['n_3'] == 0
    assert best_design['d_b3'] is None
    assert best_design['n_4'] == 0
    assert best_design['d_b3'] is None
    assert best_design['total_as'].magnitude == pytest.approx(5.15, rel=1e-3)
    assert best_design['total_bars'] == 3
    assert best_design['clear_spacing'].magnitude == pytest.approx(40, rel=1e-3)

# def test_beam_longitudinal_rebar_ACI_318_19_imperial(beam_example_imperial: RectangularBeam) -> None:
#     # This test will ensure that the longitudinal rebar design works correctly for beams defined in imperial units.
#     A_s_req = 1.5 * inch**2
#     beam_rebar = Rebar(beam_example_imperial)
#     beam_rebar.longitudinal_rebar_ACI_318_19(A_s_req=A_s_req)
#     best_design = beam_rebar.longitudinal_rebar_design

#     assert best_design is not None
#     assert best_design['n_1'] == 2
#     assert best_design['d_b1'] == 0.625 * inch
#     assert best_design['n_2'] == 1
#     assert best_design['d_b2'] == 0.5 * inch
#     assert best_design['n_3'] == 0
#     assert best_design['d_b3'] is None
#     assert best_design['n_4'] == 0
#     assert best_design['d_b3'] is None
#     assert best_design['total_as'].magnitude == pytest.approx(1.53, rel=1e-3)
#     assert best_design['total_bars'] == 3
#     assert best_design['clear_spacing'].magnitude == pytest.approx(1.5, rel=1e-3)

def test_beam_longitudinal_rebar_min_diameter(beam_example_metric: RectangularBeam) -> None:
    # This test ensures that the minimum rebar diameter constraint is respected.
    A_s_req = 2 * cm**2
    beam_rebar = Rebar(beam_example_metric)
    beam_rebar.longitudinal_rebar_ACI_318_19(A_s_req=A_s_req)
    best_design = beam_rebar.longitudinal_rebar_design

    assert best_design is not None
    assert best_design['d_b1'] >= 10 * mm
    assert best_design['d_b2'] >= 10 * mm if best_design['d_b2'] is not None else True

def test_beam_longitudinal_rebar_max_bars_per_layer(beam_example_metric: RectangularBeam) -> None:
    # This test ensures that the maximum number of bars per layer constraint is respected.
    A_s_req = 10 * cm**2
    beam_rebar = Rebar(beam_example_metric)
    beam_rebar.longitudinal_rebar_ACI_318_19(A_s_req=A_s_req)
    best_design = beam_rebar.longitudinal_rebar_design

    assert best_design is not None
    assert best_design['n_1'] + best_design['n_2'] <= beam_rebar.max_bars_per_layer
    assert best_design['n_3'] + best_design['n_4'] <= beam_rebar.max_bars_per_layer

def test_beam_longitudinal_rebar_CIRSOC_201_25(beam_example_metric: RectangularBeam) -> None:
    # This test ensures that the longitudinal rebar design works correctly for different design codes.
    beam_example_metric.concrete.design_code = "CIRSOC 201-25"
    A_s_req = 5 * cm**2
    beam_rebar = Rebar(beam_example_metric)
    beam_rebar.longitudinal_rebar(A_s_req=A_s_req)
    best_design = beam_rebar.longitudinal_rebar_design

    assert best_design is not None
    assert best_design['n_1'] == 2
    assert best_design['d_b1'] == 16 * mm
    assert best_design['n_2'] == 1
    assert best_design['d_b2'] == 12 *mm
    assert best_design['n_3'] == 0
    assert best_design['d_b3'] is None
    assert best_design['n_4'] == 0
    assert best_design['d_b3'] is None
    assert best_design['total_as'].magnitude == pytest.approx(5.15, rel=1e-3)
    assert best_design['total_bars'] == 3
    assert best_design['clear_spacing'].magnitude == pytest.approx(40, rel=1e-3)

def test_beam_longitudinal_rebar_small_area(beam_example_metric: RectangularBeam) -> None:
    # This test ensures that the rebar design handles very small required areas correctly.
    A_s_req = 0.1 * cm**2
    beam_rebar = Rebar(beam_example_metric)
    beam_rebar.longitudinal_rebar_ACI_318_19(A_s_req=A_s_req)
    best_design = beam_rebar.longitudinal_rebar_design

    assert best_design is not None
    assert best_design['total_as'] >= A_s_req

def test_beam_longitudinal_rebar_large_area(beam_example_metric: RectangularBeam) -> None:
    # This test ensures that the rebar design handles very large required areas correctly.
    A_s_req = 50 * cm**2
    beam_rebar = Rebar(beam_example_metric)
    beam_rebar.longitudinal_rebar_ACI_318_19(A_s_req=A_s_req)
    best_design = beam_rebar.longitudinal_rebar_design

    assert best_design is not None
    assert best_design['n_1'] >= 2
    assert best_design['d_b1'] >= 8 * mm

def test_beam_longitudinal_rebar_zero_area_metric_ACI(beam_example_metric: RectangularBeam) -> None:
    A_s_req = 0 * cm**2
    beam_rebar = Rebar(beam_example_metric)
    beam_rebar.longitudinal_rebar_ACI_318_19(A_s_req=A_s_req)
    best_design = beam_rebar.longitudinal_rebar_design

    assert best_design is not None
    assert best_design['total_as'].magnitude == pytest.approx(1.5707,rel=1e-3) # 2Ø10 minimum

# def test_beam_longitudinal_rebar_zero_area_imperial(beam_example_imperial: RectangularBeam) -> None:
#     A_s_req = 0 * inch**2
#     beam_rebar = Rebar(beam_example_imperial)
#     beam_rebar.longitudinal_rebar_ACI_318_19(A_s_req=A_s_req)
#     best_design = beam_rebar.longitudinal_rebar_design

#     assert best_design is not None
#     assert best_design['total_as'].magnitude == pytest.approx(1.5707,rel=1e-3) # 2#3 minimum

def test_beam_transverse_rebar_ACI_318_19_imperial(beam_example_imperial: RectangularBeam) -> None:
    f = Forces(V_z=37.727*kip)
    beam_example_imperial.set_longitudinal_rebar_bot(n1=2, d_b1=0.625*inch)
    N1=Node(beam_example_imperial, forces=f)
    results = N1.design_shear()
    assert results is not None
    assert beam_example_imperial._stirrup_n == 1
    assert beam_example_imperial._stirrup_d_b == 3/8*inch
    assert beam_example_imperial._stirrup_s_l ==  5*inch 
    assert beam_example_imperial._A_v.to('cm**2/m').magnitude == pytest.approx(11.2214,rel=1e-3)

def test_beam_transverse_rebar_ACI_318_19_metric(beam_example_metric: RectangularBeam) -> None:
    # This test ensures that the transverse rebar design works correctly for beams defined in metric units.
    f = Forces(V_z=100 * kN)
    beam_example_metric.set_longitudinal_rebar_bot(n1=2, d_b1=16 * mm)
    N1=Node(beam_example_metric, forces=f)
    results = N1.design_shear()
    
    assert results is not None
    assert beam_example_metric._stirrup_n == 1
    assert beam_example_metric._stirrup_d_b == 10 * mm
    assert beam_example_metric._stirrup_s_l == 22 * cm
    assert beam_example_metric._A_v.to('cm**2/m').magnitude == pytest.approx(7.14, rel=1e-3)

def test_beam_transverse_rebar_max_spacing_imperial(beam_example_imperial: RectangularBeam) -> None:
    # This test ensures that the maximum spacing constraints are respected.
    f = Forces(V_z=50 * kip)
    beam_example_imperial.set_longitudinal_rebar_bot(n1=2, d_b1=0.625 * inch)
    N1=Node(beam_example_imperial, forces=f)
    results = N1.design_shear()
    
    assert results is not None
    assert beam_example_imperial._stirrup_s_l <= 24 * inch
    assert beam_example_imperial._stirrup_s_w <= 24 * inch

def test_beam_transverse_rebar_min_diameter(beam_example_metric: RectangularBeam) -> None:
    # This test ensures that the minimum rebar diameter constraint is respected for transverse reinforcement.
    f = Forces(V_z=50 * kN)
    beam_example_metric.set_longitudinal_rebar_bot(n1=2, d_b1=16 * mm)
    N1=Node(beam_example_metric, forces=f)
    results = N1.design_shear()
    
    assert results is not None
    assert beam_example_metric._stirrup_d_b >= 8 * mm

def test_beam_transverse_rebar_CIRSOC_201_25(beam_example_metric: RectangularBeam) -> None:
    # This test ensures that the transverse rebar design works correctly for different design codes.
    beam_example_metric.concrete.design_code = "CIRSOC 201-25"
    f = Forces(V_z=100 * kN)
    beam_example_metric.set_longitudinal_rebar_bot(n1=2, d_b1=16 * mm)
    N1=Node(beam_example_metric, forces=f)
    results = N1.design_shear()
    
    assert results is not None
    assert beam_example_metric._stirrup_n == 1
    assert beam_example_metric._stirrup_d_b == 6 * mm
    assert beam_example_metric._stirrup_s_l == 22 * cm
    assert beam_example_metric._A_v.to('cm**2/m').magnitude == pytest.approx(2.57, rel=1e-3)

# This is where pytest will collect the tests and run them
if __name__ == "__main__":
    pytest.main()