import json
import os
from datetime import date
from typing import Any, Dict

import pytest

from datatypes.epc.schema.tests.helpers import from_dict
from datatypes.epc.surveys.pashub_rdsap_site_notes import (
    ExtensionConstruction,
    ExtensionMeasurements,
    ExtensionRoofSpace,
    PasHubRdSapSiteNotes,
)

FIXTURES = os.path.join(os.path.dirname(__file__), "fixtures")


def load(filename: str) -> Dict[str, Any]:
    with open(os.path.join(FIXTURES, filename)) as f:
        return json.load(f)  # type: ignore[no-any-return]


class TestExample1:
    """No extensions; regular boiler with hot water cylinder; natural ventilation."""

    @pytest.fixture
    def survey(self) -> PasHubRdSapSiteNotes:
        return from_dict(
            PasHubRdSapSiteNotes, load("pashub_rdsap_site_notes_example1.json")
        )

    # --- inspection_metadata ---

    def test_report_reference(self, survey: PasHubRdSapSiteNotes) -> None:
        assert (
            survey.inspection_metadata.report_reference
            == "49D422A9-0779-44DD-9665-464D35DFF1A8"
        )

    def test_created_on(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.inspection_metadata.created_on == "2026-03-31"

    def test_property_photo_absent(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.inspection_metadata.property_photo is None

    # --- general ---

    def test_transaction_type(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.general.transaction_type == "None of the Above"

    def test_number_of_extensions(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.general.number_of_extensions == 0

    def test_smart_meters(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.general.electricity_smart_meter is True
        assert survey.general.gas_smart_meter is True

    # --- building_construction ---

    def test_main_building_wall_thickness(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.building_construction.main_building.wall_thickness_mm == 280

    def test_main_building_walls_insulation_type(
        self, survey: PasHubRdSapSiteNotes
    ) -> None:
        assert (
            survey.building_construction.main_building.walls_insulation_type
            == "As built"
        )

    def test_filled_cavity_indicators_absent(
        self, survey: PasHubRdSapSiteNotes
    ) -> None:
        assert (
            survey.building_construction.main_building.filled_cavity_indicators is None
        )

    def test_no_extensions_in_construction(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.building_construction.extensions is None

    def test_floor_construction(self, survey: PasHubRdSapSiteNotes) -> None:
        assert (
            survey.building_construction.floor.floor_construction
            == "Suspended, not timber"
        )

    # --- building_measurements ---

    def test_main_building_floor_count(self, survey: PasHubRdSapSiteNotes) -> None:
        assert len(survey.building_measurements.main_building.floors) == 2

    def test_floor_area(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.building_measurements.main_building.floors[0].area_m2 == 24.78

    def test_no_extension_measurements(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.building_measurements.extensions is None

    # --- roof_space ---

    def test_roof_insulation_thickness_mm(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.roof_space.main_building.insulation_thickness_mm == 100

    def test_roof_insulation_thickness_string_absent(
        self, survey: PasHubRdSapSiteNotes
    ) -> None:
        assert survey.roof_space.main_building.insulation_thickness is None

    def test_no_extension_roof_spaces(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.roof_space.extensions is None

    def test_rooms_in_roof_false(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.roof_space.main_building.rooms_in_roof is False

    # --- windows ---

    def test_window_count(self, survey: PasHubRdSapSiteNotes) -> None:
        assert len(survey.windows) == 4

    def test_window_dimensions(self, survey: PasHubRdSapSiteNotes) -> None:
        w = survey.windows[0]
        assert w.height_m == 1.36
        assert w.width_m == 1.0

    def test_window_orientation(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.windows[0].orientation == "South East"
        assert survey.windows[2].orientation == "North West"

    def test_window_glazing_gap(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.windows[0].glazing_gap == "16 mm or more"

    # --- heating_and_hot_water ---

    def test_main_heating_manufacturer(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.heating_and_hot_water.main_heating.manufacturer == "Vaillant"

    def test_main_heating_model(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.heating_and_hot_water.main_heating.model == "ecoFIT sustain 415"

    def test_main_heating_product_id(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.heating_and_hot_water.main_heating.product_id == 18400

    def test_main_heating_type_regular(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.heating_and_hot_water.main_heating.type == "Regular"

    def test_water_heating_cylinder_present(self, survey: PasHubRdSapSiteNotes) -> None:
        wh = survey.heating_and_hot_water.water_heating
        assert wh.cylinder_size == "Normal (90-130 litres)"
        assert wh.insulation_type == "Factory fitted"
        assert wh.insulation_thickness_mm == 12
        assert wh.has_thermostat is True

    def test_secondary_heating_none(self, survey: PasHubRdSapSiteNotes) -> None:
        assert (
            survey.heating_and_hot_water.secondary_heating.secondary_fuel
            == "No Secondary Heating"
        )

    # --- ventilation ---

    def test_ventilation_type(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.ventilation.ventilation_type == "Natural"

    def test_ventilation_pcdf_absent(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.ventilation.ventilation_in_pcdf_database is None

    def test_extract_fans(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.ventilation.number_of_extract_fans == 2

    # --- room_count_elements ---

    def test_habitable_rooms(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.room_count_elements.number_of_habitable_rooms == 2

    def test_heated_rooms_zero(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.room_count_elements.number_of_heated_rooms == 0

    def test_led_bulbs(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.room_count_elements.number_of_fixed_led_bulbs == 5

    def test_cfl_bulbs(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.room_count_elements.number_of_fixed_cfl_bulbs == 4

    # --- water_use ---

    def test_shower_outlet_type(self, survey: PasHubRdSapSiteNotes) -> None:
        assert len(survey.water_use.showers) == 1
        assert survey.water_use.showers[0].outlet_type == "Non-Electric Shower"

    # --- addendum ---

    def test_addendum_value(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.addendum.addendum == "PV Recommended"

    def test_related_party_disclosure(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.addendum.related_party_disclosure == "No related party"

    def test_hard_to_treat_flags_false(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.addendum.hard_to_treat_cavity_access_issues is False
        assert survey.addendum.hard_to_treat_cavity_high_exposure is False
        assert survey.addendum.hard_to_treat_cavity_narrow_cavities is False


class TestExample2:
    """With extensions; combi boiler (no cylinder); mechanical extract ventilation."""

    @pytest.fixture
    def survey(self) -> PasHubRdSapSiteNotes:
        return from_dict(
            PasHubRdSapSiteNotes, load("pashub_rdsap_site_notes_example2.json")
        )

    # --- inspection_metadata ---

    def test_report_reference(self, survey: PasHubRdSapSiteNotes) -> None:
        assert (
            survey.inspection_metadata.report_reference
            == "6EA2A86D-94CE-4792-8D49-AB495C744EDD"
        )

    def test_property_photo_present(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.inspection_metadata.property_photo is True

    def test_created_on_differs_from_inspection_date(
        self, survey: PasHubRdSapSiteNotes
    ) -> None:
        assert survey.inspection_metadata.created_on == "2025-11-10"
        assert survey.inspection_metadata.date_of_inspection == date(2025, 9, 25)

    # --- general ---

    def test_number_of_extensions(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.general.number_of_extensions == 1

    def test_transaction_type(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.general.transaction_type == "Grant-Scheme (ECO, RHI, etc.)"

    # --- building_construction ---

    def test_main_building_filled_cavity_indicators(
        self, survey: PasHubRdSapSiteNotes
    ) -> None:
        assert (
            survey.building_construction.main_building.filled_cavity_indicators
            == "evidence of cavity fill drill holes"
        )

    def test_extension_construction_present(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.building_construction.extensions is not None
        assert len(survey.building_construction.extensions) == 1

    def test_extension_construction_type(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.building_construction.extensions is not None
        ext: ExtensionConstruction = survey.building_construction.extensions[0]
        assert ext.id == 1
        assert ext.walls_insulation_type == "As built"
        assert ext.wall_thickness_mm == 310

    def test_extension_no_filled_cavity_indicators(
        self, survey: PasHubRdSapSiteNotes
    ) -> None:
        assert survey.building_construction.extensions is not None
        assert (
            survey.building_construction.extensions[0].filled_cavity_indicators is None
        )

    # --- building_measurements ---

    def test_main_building_floor_count(self, survey: PasHubRdSapSiteNotes) -> None:
        assert len(survey.building_measurements.main_building.floors) == 2

    def test_extension_measurements_present(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.building_measurements.extensions is not None
        assert len(survey.building_measurements.extensions) == 1

    def test_extension_floor_area(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.building_measurements.extensions is not None
        ext: ExtensionMeasurements = survey.building_measurements.extensions[0]
        assert ext.id == 1
        assert len(ext.floors) == 1
        assert ext.floors[0].area_m2 == 3.8

    # --- roof_space ---

    def test_main_roof_insulation_thickness_mm(
        self, survey: PasHubRdSapSiteNotes
    ) -> None:
        assert survey.roof_space.main_building.insulation_thickness_mm == 100

    def test_extension_roof_spaces_present(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.roof_space.extensions is not None
        assert len(survey.roof_space.extensions) == 1

    def test_extension_roof_uses_string_thickness(
        self, survey: PasHubRdSapSiteNotes
    ) -> None:
        assert survey.roof_space.extensions is not None
        ext: ExtensionRoofSpace = survey.roof_space.extensions[0]
        assert ext.insulation_thickness == "As built"
        assert ext.insulation_thickness_mm is None

    def test_extension_roof_construction_type(
        self, survey: PasHubRdSapSiteNotes
    ) -> None:
        assert survey.roof_space.extensions is not None
        assert (
            survey.roof_space.extensions[0].construction_type
            == "Pitched roof, Sloping ceiling"
        )

    # --- windows ---

    def test_window_count(self, survey: PasHubRdSapSiteNotes) -> None:
        assert len(survey.windows) == 8

    def test_extension_windows(self, survey: PasHubRdSapSiteNotes) -> None:
        extension_windows = [w for w in survey.windows if w.location == "Extension 1"]
        assert len(extension_windows) == 5

    def test_window_ids_sequential(self, survey: PasHubRdSapSiteNotes) -> None:
        ids = [w.id for w in survey.windows]
        assert ids == list(range(1, 9))

    # --- heating_and_hot_water ---

    def test_main_heating_type_combi(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.heating_and_hot_water.main_heating.type == "Combi"

    def test_main_heating_model(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.heating_and_hot_water.main_heating.model == "ecoTEC pro 28"

    def test_water_heating_no_cylinder(self, survey: PasHubRdSapSiteNotes) -> None:
        wh = survey.heating_and_hot_water.water_heating
        assert wh.cylinder_size == "No Cylinder"
        assert wh.cylinder_measured_heat_loss is None
        assert wh.insulation_type is None
        assert wh.insulation_thickness_mm is None
        assert wh.has_thermostat is None

    # --- ventilation ---

    def test_ventilation_type(self, survey: PasHubRdSapSiteNotes) -> None:
        assert (
            survey.ventilation.ventilation_type == "Mechanical Extract - Decentralised"
        )

    def test_ventilation_in_pcdf_database(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.ventilation.ventilation_in_pcdf_database is False

    def test_no_extract_fans_for_mev(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.ventilation.number_of_extract_fans == 0

    # --- room_count_elements ---

    def test_habitable_rooms(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.room_count_elements.number_of_habitable_rooms == 3

    def test_heated_rooms_null(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.room_count_elements.number_of_heated_rooms is None

    def test_incandescent_bulbs(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.room_count_elements.number_of_fixed_incandescent_bulbs == 4

    # --- addendum ---

    def test_addendum_none(self, survey: PasHubRdSapSiteNotes) -> None:
        assert survey.addendum.addendum == "None"
