from recommendations.Costs import Costs
from unittest.mock import Mock
import pytest


class TestCosts:

    def test_cavity_wall_insulation(self):
        mock_property = Mock()
        mock_property.data = {
            "county": "Northamptonshire"
        }

        costs = Costs(mock_property)

        cwi_material = {
            "description": "cwi",
            "depth": 75,
            "thermal_conductivity": 0.037,
            "total_cost": 14,
            "labour_hours_per_unit": 0.065,
            "is_installer_quote": True
        }

        cwi_results = costs.cavity_wall_insulation(
            wall_area=95.9104281347967,
            material=cwi_material,
        )

        assert cwi_results == {'total': 1342.7459938871539, 'labour_hours': 8, 'labour_days': 1}

    def test_loft_insulation(self):
        mock_property = Mock()
        mock_property.data = {
            "county": "Northamptonshire"
        }

        costs = Costs(mock_property)
        loft_material = {
            "description": "Crown Loft Roll 44 glass fibre roll",
            "depth": 270,
            "thermal_conductivity": 0.044,
            "total_cost": 11,
            "labour_hours_per_unit": 0.11,
            "is_installer_quote": True,
        }

        loft_results = costs.loft_and_flat_insulation(
            floor_area=33.5,
            material=loft_material,
        )

        assert loft_results == {'total': 368.5, 'labour_hours': 8, 'labour_days': 1}

    def test_internal_wall_insulation(self):
        mock_property = Mock()
        mock_property.data = {
            "county": "Northamptonshire"
        }

        costs = Costs(mock_property)

        iwi_material = {
            "type": "internal_wall_insulation",
            "description": "Ecotherm Eco-Versal PIR Insulation Board",
            "depth": 150,
            "depth_unit": "mm",
            "cost_unit": "gbp_per_m2",
            "thermal_conductivity": 0.022,
            "thermal_conductivity_unit": "watt_per_meter_kelvin",
            "labour_hours_per_unit": 0.18,
            "total_cost": 200,
            "link": "link",
            "is_installer_quote": True
        }

        iwi_results = costs.solid_wall_insulation(
            wall_area=95.9104281347967,
            material=iwi_material,
        )

        assert iwi_results == {
            'total': 19182.085626959342, 'labour_hours': 17.263877064263404, 'labour_days': 0.5394961582582314
        }

    def test_suspended_floor_insulation(self):
        mock_property = Mock()
        mock_property.data = {
            "county": "Northamptonshire"
        }

        costs = Costs(mock_property)

        sus_floor_material = {
            'type': 'suspended_floor_insulation', 'description': 'Thermafleece CosyWool Roll',
            'depth': 140.0,
            'depth_unit': 'mm', 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.039,
            'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'prime_material_cost': 0,
            'material_cost': 11.68, 'labour_cost': 1.78, 'labour_hours_per_unit': 0.1,
            'plant_cost': 0,
            'total_cost': 13.46, 'link': 'SPONs',
            'Notes': 'Spons did not contain labour costs so we use values for similar insulations. '
                     'We use the '
                     'same values as in Crown loft roll 44, since it is also an insulation roll',
            "is_installer_quote": False
        }

        sus_floor_non_insulation_materials = [
            {'type': 'suspended_floor_demolition', 'description': 'Removal of carpet and underfelt', 'depth': 0,
             'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
             'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 3.32, 'labour_hours_per_unit': 0.11,
             'plant_cost': 0, 'total_cost': 3.32, 'link': 'SPONs',
             'Notes': 'We ignore the plant cost that is in SPONs because we assume the carpet is not scrapped and '
                      'therefore there is no need for a skip'},
            {'type': 'suspended_floor_demolition',
             'description': 'Remove boarding; withdraw nails; set aside for reuse; ground level', 'depth': 0,
             'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
             'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 9.34, 'labour_hours_per_unit': 0.3,
             'plant_cost': 0, 'total_cost': 9.34, 'link': 'SPONs', 'Notes': 0},
            {'type': 'suspended_floor_vapour_barrier', 'description': 'Visqueen High Performance Vapour Barrier',
             'depth': 0, 'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0,
             'thermal_conductivity_unit': 0, 'prime_material_cost': 0.58, 'material_cost': 1.21, 'labour_cost': 0.48,
             'labour_hours_per_unit': 0.02, 'plant_cost': 0, 'total_cost': 1.69, 'link': 'SPONs', 'Notes': 0},
            {'type': 'suspended_floor_redecoration', 'description': 'refix floorboards previously set aside',
             'depth': 0, 'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0,
             'thermal_conductivity_unit': 0, 'prime_material_cost': 0, 'material_cost': 1.54, 'labour_cost': 24.98,
             'labour_hours_per_unit': 0.74, 'plant_cost': 0, 'total_cost': 26.52, 'link': 'SPONs', 'Notes': 0},
            {'type': 'suspended_floor_redecoration', 'description': 'Fitting carpet', 'depth': 0, 'depth_unit': 0,
             'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
             'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 6.59, 'labour_hours_per_unit': 0.37,
             'plant_cost': 0, 'total_cost': 6.59, 'link': 'SPONs',
             'Notes': 'SPONs does not have data on re-fitting the carpet so we use the data in Fitted carpeting; '
                      'Gradus woven polypropylene tufted loop\n\n as a baseline. We assume re-use of carpets, '
                      'therefore we need just labour rates'}]

        sus_floor_results = costs.suspended_floor_insulation(
            insulation_floor_area=33.5,
            material=sus_floor_material,
            non_insulation_materials=sus_floor_non_insulation_materials
        )

        assert sus_floor_results == {
            'total': 3337.07436, 'subtotal': 2780.8953, 'vat': 556.17906, 'contingency': 370.78604,
            'preliminaries': 185.39302, 'material': 483.405, 'profit': 370.78604, 'labour_hours': 54.940000000000005,
            'labour_days': 2.289166666666667, 'labour_cost': 1370.5252
        }

    def test_solid_floor_insulation(self):
        mock_property = Mock()
        mock_property.data = {
            "county": "Northamptonshire"
        }

        costs = Costs(mock_property)
        sol_floor_material = {
            'type': 'solid_floor_insulation', 'description': 'Kay-Cel Expanded Polystyrene Board',
            'depth': 100.0, 'depth_unit': 'mm', 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.033,
            'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'prime_material_cost': 0,
            'material_cost': 12.02, 'labour_cost': 4.4, 'labour_hours_per_unit': 0.19, 'plant_cost': 0,
            'total_cost': 16.42, 'link': 'SPONs', 'Notes': 0, "is_installer_quote": False
        }

        sol_floor_non_insulation_materials = [
            {'type': 'solid_floor_demolition', 'description': 'Removal of carpet and underfelt', 'depth': 0,
             'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
             'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 3.32, 'labour_hours_per_unit': 0.11,
             'plant_cost': 0, 'total_cost': 3.32, 'link': 'SPONs',
             'Notes': 'We ignore the plant cost that is in SPONs because we assume the carpet is not scrapped and '
                      'therefore there is no need for a skip'},
            {'type': 'solid_floor_preparation',
             'description': 'clean surface of concrete to receive new damp-proof membrane', 'depth': 0,
             'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
             'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 4.36, 'labour_hours_per_unit': 0.14,
             'plant_cost': 0, 'total_cost': 4.36, 'link': 0, 'Notes': 0}, {
                'type': 'solid_floor_preparation',
                'description': 'Clean out crack to '
                               'form a 20mm×20mm '
                               'groove and fill with '
                               'cement: mortar mixed '
                               'with bonding agent',
                'depth': 0, 'depth_unit': 0,
                'cost_unit': 0,
                'thermal_conductivity': 0,
                'thermal_conductivity_unit': 0,
                'prime_material_cost': 0,
                'material_cost': 6.91,
                'labour_cost': 18.99,
                'labour_hours_per_unit': 0.61,
                'plant_cost': 0.16,
                'total_cost': 26.06, 'link': 0,
                'Notes': 'This step is the '
                         'assessment and repair of '
                         'any damage to the concrete '
                         'floor such as filling '
                         'cracks or levelling uneven '
                         'areas'},
            {'type': 'solid_floor_vapour_barrier', 'description': 'Visqueen High Performance Vapour Barrier',
             'depth': 0, 'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0,
             'thermal_conductivity_unit': 0, 'prime_material_cost': 0.58, 'material_cost': 1.21, 'labour_cost': 0.48,
             'labour_hours_per_unit': 0.02, 'plant_cost': 0, 'total_cost': 1.69, 'link': 'SPONs', 'Notes': 0},
            {'type': 'solid_floor_redecoration',
             'description': 'Screeded beds; protection to compressible formwork exceeding 600mm wide', 'depth': 0,
             'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
             'prime_material_cost': 9.6, 'material_cost': 9.89, 'labour_cost': 2.67, 'labour_hours_per_unit': 0.15,
             'plant_cost': 0, 'total_cost': 12.56, 'link': 'SPONs',
             'Notes': 'This is the screed layer, placed on top of the insulation'},
            {'type': 'solid_floor_redecoration', 'description': 'Fitting carpet', 'depth': 0, 'depth_unit': 0,
             'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
             'prime_material_cost': 0, 'material_cost': 0, 'labour_cost': 6.59, 'labour_hours_per_unit': 0.37,
             'plant_cost': 0, 'total_cost': 6.59, 'link': 'SPONs',
             'Notes': 'SPONs does not have data on re-fitting the carpet so we use the data in Fitted carpeting; '
                      'Gradus woven polypropylene tufted loop\n\n as a baseline. We assume re-use of carpets, '
                      'therefore we need just labour rates'},
            {'type': 'solid_floor_redecoration',
             'description': 'Fitting existing softwood skirting or architrave to new frames; 150mm high', 'depth': 0,
             'depth_unit': 0, 'cost_unit': 0, 'thermal_conductivity': 0, 'thermal_conductivity_unit': 0,
             'prime_material_cost': 0, 'material_cost': 0.01, 'labour_cost': 4.87, 'labour_hours_per_unit': 0.12,
             'plant_cost': 0, 'total_cost': 4.88, 'link': 'SPONs', 'Notes': 0}
        ]

        sol_floor_results = costs.solid_floor_insulation(
            insulation_floor_area=33.5,
            material=sol_floor_material,
            non_insulation_materials=sol_floor_non_insulation_materials
        )

        assert sol_floor_results == {
            'total': 4245.023520000001, 'subtotal': 3537.5196, 'vat': 707.5039200000001, 'contingency': 471.66928,
            'preliminaries': 235.83464, 'material': 1006.3399999999999, 'profit': 471.66928, 'labour_hours': 57.285,
            'labour_days': 2.386875, 'labour_cost': 1346.6464
        }

    def test_external_wall_insulation(self):
        mock_property = Mock()
        mock_property.data = {
            "county": "Northamptonshire",
            "property-type": "House",
            "built-form": 'End-Terrace'
        }

        costs = Costs(mock_property)

        ewi_material = {
            'type': 'external_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board',
            'depth': 150.0, 'depth_unit': 'mm', 'cost_unit': 'gbp_per_m2', 'thermal_conductivity': 0.022,
            'thermal_conductivity_unit': 'watt_per_meter_kelvin',
            'labour_hours_per_unit': 1.4,
            'total_cost': 300, 'link': 'SPONs', 'Notes': 0, "is_installer_quote": True
        }

        ewi_results = costs.solid_wall_insulation(
            wall_area=95.9104281347967,
            material=ewi_material,
        )

        assert ewi_results == {
            'total': 28773.12844043901, 'labour_hours': 134.2745993887154, 'labour_days': 4.196081230897356
        }

    def test_flat_roof_insulation(self):
        mock_property = Mock()
        mock_property.data = {
            "county": "Northamptonshire"
        }

        costs = Costs(mock_property)
        flat_roof_material = {
            'id': 1225, 'type': 'flat_roof_insulation',
            'description': 'Kingspan Thermaroof TR21 zero OPD '
                           'urethene insulation board',
            'depth': 100.0, 'depth_unit': 'mm', 'cost': None,
            'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.04,
            'r_value_unit': 'square_meter_kelvin_per_watt',
            'thermal_conductivity': 0.025,
            'thermal_conductivity_unit': 'watt_per_meter_kelvin',
            'link': 'SPONs',
            'created_at': "now", 'is_active': True,
            'prime_material_cost': None, 'material_cost': 50.95,
            'labour_cost': 10.66, 'labour_hours_per_unit': 0.48,
            'plant_cost': 0.0, 'total_cost': 61.61,
            'notes': "SPONs didn't have a labour hours so we use "
                     "0.48 which is similar to other materials",
            "is_installer_quote": False
        }

        flat_roof_floor_results = costs.loft_and_flat_insulation(
            floor_area=33.5,
            material=flat_roof_material,
        )

        assert flat_roof_floor_results == {
            'total': 2063.935, 'subtotal': 1719.9458333333334, 'vat': 343.9891666666665, 'labour_hours': 8,
            'labour_days': 1
        }

        assert costs.labour_adjustment_factor == 0.88

    # Test for different wattages
    @pytest.mark.parametrize("n_panels, expected_cost", [
        (7, 5458.727999999999),
        (10, 6013.139999999999),
        (12, 6386.447999999999),
        (15, 7594.451999999999),
    ])
    def test_solar_pv_different_wattages(self, n_panels, expected_cost):
        mock_property = Mock()
        mock_property.data = {"county": "Mansfield"}
        costs = Costs(mock_property)
        result = costs.solar_pv(n_panels)
        assert result['total'] == pytest.approx(expected_cost, rel=0.01)
