import pytest
from unittest.mock import Mock
from epc_api.client import EpcClient
from backend.Property import Property
from etl.epc_clean.EpcClean import EpcClean

# Define some test data
mock_epc_response = {
    "rows": [
        {
            "lmk-key": 1,
            "uprn": 1,
            "number-habitable-rooms": 5,
            "property-type": "House",
            "built-form": "Detached",
            "inspection-date": "2023-06-01",
            'lodgement-datetime': '2023-06-01 20:29:01',
            "some-other-key": "some-value",
            "roof-description": "Roof Description",
            "walls-description": "Walls Description",
            "windows-description": "Windows Description",
            "mainheat-description": "Main Heating Description",
            "hotwater-description": "Hot Water Description",
            "transaction-type": "rental",
            "lighting-description": "Good Lighting Efficiency",
            "energy-consumption-current": "50",
            "co2-emissions-current": "123",
            "mechanical-ventilation": "natural",
            'photo-supply': 0,
            "solar-water-heating-flag": "N",
            "wind-turbine-count": 0,
            "extension-count": 0,
            "heat-loss-corridor": "no corridor",
            "unheated-corridor-length": 0,
            "mains-gas-flag": "Y",
            "floor-height": 2.5,
            "total-floor-area": 100,
            "construction-age-band": "England and Wales: 1967-1975",
            "floor-description": "Floor Description"
        },
        {
            "lmk-key": 2,
            "uprn": 2,
            "number-habitable-rooms": 5,
            "property-type": "House",
            "built-form": "Detached",
            "inspection-date": "2023-05-01",
            'lodgement-datetime': '2023-05-01 20:29:01',
            "some-other-key": "some-other-value",
            "roof-description": "Roof Description",
            "walls-description": "Walls Description",
            "windows-description": "Windows Description",
            "mainheat-description": "Main Heating Description",
            "hotwater-description": "Hot Water Description",
            "transaction-type": "rental",
            "lighting-description": "Good Lighting Efficiency",
            "energy-consumption-current": "50",
            "co2-emissions-current": "123",
            "mechanical-ventilation": "natural",
            'photo-supply': 0,
            "solar-water-heating-flag": "N",
            "wind-turbine-count": 0,
            "extension-count": 0,
            "heat-loss-corridor": "no corridor",
            "unheated-corridor-length": 0,
            "mains-gas-flag": "Y",
            "floor-height": 2.5,
            "total-floor-area": 100,
            "construction-age-band": "England and Wales: 1967-1975",
            "floor-description": "Floor Description"
        }
    ]
}

mock_epc_response_dupe = {
    'rows': [
        {
            "lmk-key": 1,
            "uprn": 1,
            "number-habitable-rooms": 5,
            "property-type": "House",
            'inspection-date': '2023-06-01',
            'lodgement-datetime': '2023-06-01 20:29:01',
            'some-other-key': 'some-value', 'roof-description': 'Roof Description',
            'walls-description': 'Walls Description', 'windows-description': 'Windows Description',
            'mainheat-description': 'Main Heating Description', 'hotwater-description': 'Hot Water Description',
            "transaction-type": "rental",
            "lighting-description": "Good Lighting Efficiency",
            "energy-consumption-current": "50",
            "co2-emissions-current": "123",
            "mechanical-ventilation": "natural",
            'photo-supply': 0,
            "solar-water-heating-flag": "N",
            "wind-turbine-count": 0,
            "extension-count": 0,
            "heat-loss-corridor": "no corridor",
            "unheated-corridor-length": 0,
            "mains-gas-flag": "Y",
            "floor-height": 2.5,
            "total-floor-area": 100,
            "construction-age-band": "England and Wales: 1967-1975",
            "floor-description": "Floor Description"
        },
        {
            "lmk-key": 2,
            "uprn": 2,
            "number-habitable-rooms": 5,
            "property-type": "House",
            'inspection-date': '2023-05-01',
            'lodgement-datetime': '2023-05-01 20:29:01',
            'some-other-key': 'some-other-value',
            'roof-description': 'Roof Description', 'walls-description': 'Walls Description',
            'windows-description': 'Windows Description', 'mainheat-description': 'Main Heating Description',
            'hotwater-description': 'Hot Water Description',
            "transaction-type": "rental",
            "lighting-description": "Good Lighting Efficiency",
            "energy-consumption-current": "50",
            "co2-emissions-current": "123",
            "mechanical-ventilation": "natural",
            'photo-supply': 0,
            "solar-water-heating-flag": "N",
            "wind-turbine-count": 0,
            "extension-count": 0,
            "heat-loss-corridor": "no corridor",
            "unheated-corridor-length": 0,
            "mains-gas-flag": "Y",
            "floor-height": 2.5,
            "total-floor-area": 100,
            "construction-age-band": "England and Wales: 1967-1975",
            "floor-description": "Floor Description"
        },
        {
            "lmk-key": 3,
            "uprn": 3,
            "number-habitable-rooms": 5,
            "property-type": "House",
            'inspection-date': '2023-06-01',
            'lodgement-datetime': '2023-06-01 20:29:01',
            'some-other-key': 'duplicate-date',
            'roof-description': 'Roof Description',
            'walls-description': 'Walls Description', 'windows-description': 'Windows Description',
            'mainheat-description': 'Main Heating Description', 'hotwater-description': 'Hot Water Description',
            "transaction-type": "rental",
            "lighting-description": "Good Lighting Efficiency",
            "energy-consumption-current": "50",
            "co2-emissions-current": "123",
            "mechanical-ventilation": "natural",
            'photo-supply': 0,
            "solar-water-heating-flag": "N",
            "wind-turbine-count": 0,
            "extension-count": 0,
            "heat-loss-corridor": "no corridor",
            "unheated-corridor-length": 0,
            "mains-gas-flag": "Y",
            "floor-height": 2.5,
            "total-floor-area": 100,
            "construction-age-band": "England and Wales: 1967-1975",
            "floor-description": "Floor Description"
        }
    ]
}


class TestProperty:
    @pytest.fixture(autouse=True)
    def property_instance(self, mock_epc_client, mock_cleaner):
        property_instance = Property(1, "AB12CD", "Test Address", epc_client=mock_epc_client)
        return property_instance

    @pytest.fixture(autouse=True)
    def property_instance_dupe_data(self, mock_epc_client_dupe_data):
        property_instance_dupe_data = Property(2, "AB12CD", "Test Address", epc_client=mock_epc_client_dupe_data)
        return property_instance_dupe_data

    @pytest.fixture
    def mock_epc_client(self):
        mock_epc_client = Mock(spec=EpcClient(auth_token="mocked_auth_token"))
        mock_epc_client.domestic.search.return_value = mock_epc_response.copy()
        mock_epc_client.auth_token = "mocked_auth_token"
        return mock_epc_client

    @pytest.fixture
    def mock_epc_client_dupe_data(self):
        mock_epc_client_dupe_data = Mock(spec=EpcClient(auth_token="mocked_auth_token"))
        mock_epc_client_dupe_data.domestic.search.return_value = mock_epc_response_dupe.copy()
        mock_epc_client_dupe_data.auth_token = "mocked_auth_token"
        return mock_epc_client_dupe_data

    @pytest.fixture
    def mock_cleaner(self):
        lighting_averages = [
            {'lighting-description': 'good lighting efficiency', 'low-energy-lighting': 99.26666666666667},
            {'lighting-description': 'excellent lighting efficiency', 'low-energy-lighting': 100.0},
            {'lighting-description': 'below average lighting efficiency', 'low-energy-lighting': 0.0}
        ]

        cleaner_spec = EpcClean(
            data=[
                {"roof-description": "Roof Description"},
                {"walls-description": "Walls Description"},
                {"windows-description": "Windows Description"},
                {"mainheat-description": "Main Heating Description"},
                {"hotwater-description": "Hot Water Description"},
                {"lighting-description": "Good Lighting Efficiency"},
                {"low-energy-lighting": 0},
                {"floor-description": "Floor Description"}
            ],
            lighting_averages=lighting_averages
        )

        mock_cleaner = Mock(spec=cleaner_spec)

        walls_data = {
            "original_description": "Walls Description",
            "is_cavity_wall": True,
            "is_solid_brick": False,
            "is_timber_frame": False,
            "is_system_built": False,
            "is_park_home": False,
            "is_cob": False,
            "is_sandstone_or_limestone": False,
            "is_granite_or_whinstone": False,
        }

        mock_cleaner.cleaned = {
            "roof-description": [{"original_description": "Roof Description"}],
            "walls-description": [walls_data],
            "windows-description": [{"original_description": "Windows Description"}],
            "mainheat-description": [{"original_description": "Main Heating Description"}],
            "hotwater-description": [{"original_description": "Hot Water Description"}],
            "lighting-description": [{"original_description": "Good Lighting Efficiency"}],
            "floor-description": [
                {"original_description": "Floor Description", "is_suspended": True, "another_property_below": False}]
        }
        return mock_cleaner

    def test_init(self, mock_epc_client):
        inst1 = Property(0, "AB12CD", "Test Address", epc_client=mock_epc_client)
        # Should be mocked auth token
        assert inst1.epc_client.auth_token == "mocked_auth_token"

        inst2 = Property(3, "AB12CD", "Test Address", epc_client=mock_epc_client)
        assert inst2.epc_client.auth_token

        inst3 = Property(4, "AB12CD", "Test Address", data={"some": "data"}, epc_client=mock_epc_client)
        assert inst3.data == {"some": "data"}

        data = inst3.search_address_epc()
        assert data is None

    def test_search_address_epc(self, property_instance):
        # Call the method to test
        property_instance.search_address_epc()

        # Verify that the correct data is being returned
        assert property_instance.data == mock_epc_response["rows"][0]

    def test_search_address_epc_multiple_results(self, property_instance_dupe_data, mock_epc_client_dupe_data):
        with pytest.raises(Exception, match="More than one result found for this address - investigate me"):
            property_instance_dupe_data.search_address_epc()

    def test_get_components(self, property_instance, mock_cleaner, mock_epc_client):
        property_instance.search_address_epc()
        property_instance.get_components(mock_cleaner.cleaned)

        # Verify that the components are set correctly
        assert property_instance.roof == {"original_description": "Roof Description"}
        assert property_instance.walls == {
            "original_description": "Walls Description",
            "is_cavity_wall": True,
            "is_solid_brick": False,
            "is_timber_frame": False,
            "is_system_built": False,
            "is_park_home": False,
            "is_cob": False,
            "is_sandstone_or_limestone": False,
            "is_granite_or_whinstone": False,
        }
        assert property_instance.windows == {"original_description": "Windows Description"}
        assert property_instance.main_heating == {"original_description": "Main Heating Description"}
        assert property_instance.hotwater == {"original_description": "Hot Water Description"}

        assert property_instance.wall_type == "cavity"

    def test_get_components_without_cleaned_data(self, property_instance, mock_cleaner):
        # Modify the mock EpcClean to not have cleaned data
        mock_cleaner.cleaned = {}

        # Verify that ValueError is raised when EpcClean doesn't contain cleaned data
        with pytest.raises(ValueError, match="Cleaner does not contain cleaned data"):
            property_instance.get_components(mock_cleaner.cleaned)

    def test_get_components_no_data(self, property_instance, mock_cleaner):
        # Modify the mock cleaner to have no attributes for a specific description
        mock_cleaner.cleaned = {
            "roof-description": []
        }

        # Verify that ValueError is raised when no attributes are found
        with pytest.raises(ValueError, match="Property does not contain data"):
            property_instance.get_components(mock_cleaner.cleaned)

    def test_get_components_no_attributes(self, property_instance, mock_cleaner):
        # Modify the mock cleaner to have no attributes for a specific description
        mock_cleaner.cleaned = {
            "roof-description": []
        }
        property_instance.search_address_epc()
        property_instance.data["roof-description"] = "Pitched, no insulation"
        property_instance.walls = {
            "original_description": "Walls Description",
            "is_cavity_wall": True,
            "is_solid_brick": False,
            "is_timber_frame": False,
            "is_system_built": False,
            "is_park_home": False,
            "is_cob": False,
            "is_sandstone_or_limestone": False,
            "is_granite_or_whinstone": False,
        }

        property_instance.floor = {
            "is_suspended": False,
            "another_property_below": False,
            "is_solid": True
        }

        # Assert backup cleaning has been applied
        property_instance.get_components(mock_cleaner.cleaned)

        assert property_instance.roof["clean_description"] == "Pitched, no insulation"
        assert property_instance.roof["is_pitched"]

    def test_get_components_multiple_attributes(self, property_instance, mock_cleaner):
        # This shouldn't happen - it would mean a cleaning error
        property_instance.search_address_epc()
        property_instance.data["roof-description"] = "Roof Description"
        cleaned = {
            "roof-description": [
                {"original_description": "Roof Description"},
                {"original_description": "Roof Description"}
            ]
        }

        # Verify that ValueError is raised when multiple attributes are found
        with pytest.raises(ValueError, match="Either No attributes or multiple found for roof-description"):
            property_instance.get_components(cleaned)
