import json
import os
from datetime import date

import pytest

from backend.documents_parser.elmhurst_extractor import ElmhurstSiteNotesExtractor
from datatypes.epc.domain.epc_property_data import EpcPropertyData
from datatypes.epc.domain.mapper import EpcPropertyDataMapper

FIXTURE_PATH = os.path.join(
    os.path.dirname(__file__), "fixtures", "elmhurst_site_notes_1_text.json"
)
FIXTURE_PATH_2 = os.path.join(
    os.path.dirname(__file__), "fixtures", "elmhurst_site_notes_2_text.json"
)


@pytest.fixture(scope="module")
def result() -> EpcPropertyData:
    with open(FIXTURE_PATH) as f:
        pages = json.load(f)
    site_notes = ElmhurstSiteNotesExtractor(pages).extract()
    return EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes)


@pytest.fixture(scope="module")
def result2() -> EpcPropertyData:
    with open(FIXTURE_PATH_2) as f:
        pages = json.load(f)
    site_notes = ElmhurstSiteNotesExtractor(pages).extract()
    return EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes)


class TestAddress:
    def test_address_line_1(self, result: EpcPropertyData) -> None:
        assert result.address_line_1 == "19, Queens Road"

    def test_post_town(self, result: EpcPropertyData) -> None:
        assert result.post_town == "BURNLEY"

    def test_postcode(self, result: EpcPropertyData) -> None:
        assert result.postcode == "BB10 1XX"


class TestInspectionInfo:
    def test_inspection_date(self, result: EpcPropertyData) -> None:
        assert result.inspection_date == date(2026, 3, 6)

    def test_tenure(self, result: EpcPropertyData) -> None:
        assert result.tenure == "Rented (social)"

    def test_transaction_type(self, result: EpcPropertyData) -> None:
        assert result.transaction_type == "Grant scheme"

    def test_report_reference(self, result: EpcPropertyData) -> None:
        assert result.report_reference == "P960-0001-001573"


class TestPropertyDescription:
    def test_property_type(self, result: EpcPropertyData) -> None:
        assert result.property_type == "Bungalow"

    def test_built_form(self, result: EpcPropertyData) -> None:
        assert result.built_form == "End-Terrace"

    def test_dwelling_type(self, result: EpcPropertyData) -> None:
        assert result.dwelling_type == "End-Terrace bungalow"

    def test_number_of_storeys(self, result: EpcPropertyData) -> None:
        assert result.number_of_storeys == 1

    def test_has_conservatory(self, result: EpcPropertyData) -> None:
        assert result.has_conservatory is False

    def test_total_floor_area(self, result: EpcPropertyData) -> None:
        assert result.total_floor_area_m2 == 44.89


class TestCounts:
    def test_habitable_rooms_count(self, result: EpcPropertyData) -> None:
        assert result.habitable_rooms_count == 2

    def test_heated_rooms_count(self, result: EpcPropertyData) -> None:
        assert result.heated_rooms_count == 2

    def test_door_count(self, result: EpcPropertyData) -> None:
        assert result.door_count == 0

    def test_insulated_door_count(self, result: EpcPropertyData) -> None:
        assert result.insulated_door_count == 0

    def test_open_chimneys_count(self, result: EpcPropertyData) -> None:
        assert result.open_chimneys_count == 0

    def test_blocked_chimneys_count(self, result: EpcPropertyData) -> None:
        assert result.blocked_chimneys_count == 0


class TestLighting:
    def test_led_count(self, result: EpcPropertyData) -> None:
        assert result.led_fixed_lighting_bulbs_count == 4

    def test_cfl_count(self, result: EpcPropertyData) -> None:
        assert result.cfl_fixed_lighting_bulbs_count == 4

    def test_incandescent_count(self, result: EpcPropertyData) -> None:
        assert result.incandescent_fixed_lighting_bulbs_count == 0


class TestFlags:
    def test_solar_water_heating(self, result: EpcPropertyData) -> None:
        assert result.solar_water_heating is False

    def test_has_hot_water_cylinder(self, result: EpcPropertyData) -> None:
        assert result.has_hot_water_cylinder is False

    def test_has_fixed_air_conditioning(self, result: EpcPropertyData) -> None:
        assert result.has_fixed_air_conditioning is False

    def test_hydro(self, result: EpcPropertyData) -> None:
        assert result.hydro is False

    def test_photovoltaic_array(self, result: EpcPropertyData) -> None:
        assert result.photovoltaic_array is False


class TestBuildingPart:
    def test_single_building_part(self, result: EpcPropertyData) -> None:
        assert len(result.sap_building_parts) == 1

    def test_identifier(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].identifier == "main"

    def test_construction_age_band(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].construction_age_band == "1950-1966"

    def test_wall_construction(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].wall_construction == "Cavity"

    def test_wall_insulation_type(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].wall_insulation_type == "Filled Cavity"

    def test_wall_thickness_measured(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].wall_thickness_measured is True

    def test_wall_thickness_mm(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].wall_thickness_mm == 300

    def test_roof_insulation_location(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].roof_insulation_location == "Joists"

    def test_roof_insulation_thickness(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].roof_insulation_thickness == 270

    def test_floor_type(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].floor_type == "Ground floor"

    def test_floor_construction_type(self, result: EpcPropertyData) -> None:
        assert (
            result.sap_building_parts[0].floor_construction_type
            == "Suspended, not timber"
        )

    def test_floor_insulation_type_str(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].floor_insulation_type_str == "As built"

    def test_floor_u_value_known(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].floor_u_value_known is False

    def test_single_floor_dimension(self, result: EpcPropertyData) -> None:
        assert len(result.sap_building_parts[0].sap_floor_dimensions) == 1

    def test_floor_dimension_area(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].sap_floor_dimensions[0].total_floor_area_m2 == 44.89

    def test_floor_dimension_room_height(self, result: EpcPropertyData) -> None:
        assert result.sap_building_parts[0].sap_floor_dimensions[0].room_height_m == 2.24

    def test_floor_dimension_heat_loss_perimeter(self, result: EpcPropertyData) -> None:
        assert (
            result.sap_building_parts[0].sap_floor_dimensions[0].heat_loss_perimeter_m
            == 20.10
        )

    def test_floor_dimension_party_wall_length(self, result: EpcPropertyData) -> None:
        assert (
            result.sap_building_parts[0].sap_floor_dimensions[0].party_wall_length_m
            == 6.70
        )


class TestWindows:
    def test_window_count(self, result: EpcPropertyData) -> None:
        assert len(result.sap_windows) == 4

    def test_first_window_width(self, result: EpcPropertyData) -> None:
        assert result.sap_windows[0].window_width == 1.30

    def test_first_window_height(self, result: EpcPropertyData) -> None:
        assert result.sap_windows[0].window_height == 1.10

    def test_first_window_orientation(self, result: EpcPropertyData) -> None:
        assert result.sap_windows[0].orientation == "North"

    def test_first_window_glazing_type(self, result: EpcPropertyData) -> None:
        assert result.sap_windows[0].glazing_type == "Double post or during 2022"

    def test_first_window_draught_proofed(self, result: EpcPropertyData) -> None:
        assert result.sap_windows[0].draught_proofed is True

    def test_third_window_orientation(self, result: EpcPropertyData) -> None:
        assert result.sap_windows[2].orientation == "South"

    def test_frame_factor(self, result: EpcPropertyData) -> None:
        assert result.sap_windows[0].frame_factor == 0.7

    def test_transmission_u_value(self, result: EpcPropertyData) -> None:
        assert result.sap_windows[0].window_transmission_details is not None
        assert result.sap_windows[0].window_transmission_details.u_value == 1.4

    def test_transmission_solar_transmittance(self, result: EpcPropertyData) -> None:
        assert result.sap_windows[0].window_transmission_details is not None
        assert result.sap_windows[0].window_transmission_details.solar_transmittance == 0.72

    def test_transmission_data_source(self, result: EpcPropertyData) -> None:
        assert result.sap_windows[0].window_transmission_details is not None
        assert result.sap_windows[0].window_transmission_details.data_source == "Manufacturer"


class TestHeating:
    def test_single_heating_detail(self, result: EpcPropertyData) -> None:
        assert len(result.sap_heating.main_heating_details) == 1

    def test_fuel_type(self, result: EpcPropertyData) -> None:
        assert result.sap_heating.main_heating_details[0].main_fuel_type == "Mains gas"

    def test_heat_emitter_type(self, result: EpcPropertyData) -> None:
        assert (
            result.sap_heating.main_heating_details[0].heat_emitter_type == "Radiators"
        )

    def test_emitter_temperature(self, result: EpcPropertyData) -> None:
        assert (
            result.sap_heating.main_heating_details[0].emitter_temperature == "Unknown"
        )

    def test_fan_flue_present(self, result: EpcPropertyData) -> None:
        assert result.sap_heating.main_heating_details[0].fan_flue_present is True

    def test_has_fghrs(self, result: EpcPropertyData) -> None:
        assert result.sap_heating.main_heating_details[0].has_fghrs is False

    def test_main_heating_control(self, result: EpcPropertyData) -> None:
        assert (
            result.sap_heating.main_heating_details[0].main_heating_control
            == "Programmer, room thermostat and TRVs"
        )

    def test_shower_outlet_type(self, result: EpcPropertyData) -> None:
        assert result.sap_heating.shower_outlets is not None
        assert (
            result.sap_heating.shower_outlets.shower_outlet.shower_outlet_type
            == "Electric shower"
        )

    def test_no_hot_water_cylinder_size(self, result: EpcPropertyData) -> None:
        assert result.sap_heating.cylinder_size is None

    def test_has_fixed_air_conditioning(self, result: EpcPropertyData) -> None:
        assert result.sap_heating.has_fixed_air_conditioning is False

    def test_water_heating_code(self, result: EpcPropertyData) -> None:
        assert result.sap_heating.water_heating_code == 901


class TestEnergySource:
    def test_mains_gas(self, result: EpcPropertyData) -> None:
        assert result.sap_energy_source.mains_gas is True

    def test_meter_type(self, result: EpcPropertyData) -> None:
        assert result.sap_energy_source.meter_type == "Single"

    def test_electricity_smart_meter(self, result: EpcPropertyData) -> None:
        assert result.sap_energy_source.electricity_smart_meter_present is False

    def test_gas_smart_meter(self, result: EpcPropertyData) -> None:
        assert result.sap_energy_source.gas_smart_meter_present is False

    def test_wind_turbines_count(self, result: EpcPropertyData) -> None:
        assert result.sap_energy_source.wind_turbines_count == 0

    def test_wind_turbines_terrain_type(self, result: EpcPropertyData) -> None:
        assert result.sap_energy_source.wind_turbines_terrain_type == "Suburban"

    def test_pv_battery_count(self, result: EpcPropertyData) -> None:
        assert result.sap_energy_source.pv_battery_count == 0


class TestVentilation:
    def test_draught_lobby(self, result: EpcPropertyData) -> None:
        assert result.sap_ventilation is not None
        assert result.sap_ventilation.draught_lobby is False

    def test_pressure_test(self, result: EpcPropertyData) -> None:
        assert result.sap_ventilation is not None
        assert result.sap_ventilation.pressure_test == "Not available"

    def test_extract_fans_count(self, result: EpcPropertyData) -> None:
        assert result.sap_ventilation is not None
        assert result.sap_ventilation.extract_fans_count == 2

    def test_open_flues_count(self, result: EpcPropertyData) -> None:
        assert result.sap_ventilation is not None
        assert result.sap_ventilation.open_flues_count == 0


class TestDraughtproofingAndWater:
    def test_percent_draughtproofed(self, result: EpcPropertyData) -> None:
        assert result.percent_draughtproofed == 100

    def test_waste_water_heat_recovery_absent(self, result: EpcPropertyData) -> None:
        assert result.waste_water_heat_recovery == "None"

    def test_any_unheated_rooms_false(self, result: EpcPropertyData) -> None:
        assert result.any_unheated_rooms is False


class TestEnergyPerformance:
    def test_energy_rating_current(self, result: EpcPropertyData) -> None:
        assert result.energy_rating_current == 69

    def test_energy_rating_potential(self, result: EpcPropertyData) -> None:
        assert result.energy_rating_potential == 77

    def test_environmental_impact_current(self, result: EpcPropertyData) -> None:
        assert result.environmental_impact_current == 76

    def test_environmental_impact_potential(self, result: EpcPropertyData) -> None:
        assert result.environmental_impact_potential == 81

    def test_co2_emissions_current(self, result: EpcPropertyData) -> None:
        assert result.co2_emissions_current == 1.683


class TestWindowFrameMaterial:
    def test_frame_material_from_elmhurst(self, result2: EpcPropertyData) -> None:
        assert result2.sap_windows[0].frame_material == "PVC"

    def test_glazing_gap_from_elmhurst(self, result2: EpcPropertyData) -> None:
        assert result2.sap_windows[0].glazing_gap == "16 mm or more"


class TestLowEnergyLighting:
    def test_low_energy_fixed_lighting_bulbs_count(self, result2: EpcPropertyData) -> None:
        assert result2.low_energy_fixed_lighting_bulbs_count == 5
