import pytest

from recommendations.optimiser.funding_optimiser import (
    build_heat_pump_paths,
    run_optimizer,
)


def test_build_heat_pump_paths():
    eg1 = build_heat_pump_paths([], ["loft_insulation"])
    assert eg1 == [{'AND': ['loft_insulation', 'air_source_heat_pump']}]

    eg2 = build_heat_pump_paths(
        ["internal_wall_insulation", "external_wall_insulation"],
        ["loft_insulation"],
    )

    assert eg2 == [
        {'AND': ['internal_wall_insulation', 'loft_insulation', 'air_source_heat_pump']},
        {'AND': ['external_wall_insulation', 'loft_insulation', 'air_source_heat_pump']},
    ]


def test_run_optimizer_empty_input():
    solution, cost, gain = run_optimizer([])
    assert solution is None
    assert cost == 0.0
    assert gain == 0.0


def test_budget_and_target_are_passed_correctly(monkeypatch):
    captured = {}

    class FakeStrategicOptimiser:
        def __init__(
            self,
            components,
            budget=None,
            target_gain=None,
            allow_slack=False,
            verbose=False,
        ):
            captured["components"] = components
            captured["budget"] = budget
            captured["target_gain"] = target_gain
            captured["allow_slack"] = allow_slack

            self.solution = [{"cost": 100, "gain": 5}]
            self.solution_cost = 100
            self.solution_gain = 5

        def solve(self):
            pass

    monkeypatch.setattr(
        "recommendations.optimiser.funding_optimiser.StrategicOptimiser",
        FakeStrategicOptimiser,
    )

    solution, cost, gain = run_optimizer(
        [[{"cost": 100, "gain": 5}]],
        budget=500,
        sub_target_gain=10,
        allow_slack=True,
    )

    assert captured["budget"] == 500
    assert captured["target_gain"] == 10
    assert captured["allow_slack"] is True

    assert cost == 100
    assert gain == 5
    assert solution == [{"cost": 100, "gain": 5}]


def test_sub_target_gain_zero_is_passed_as_zero(monkeypatch):
    captured = {}

    class FakeStrategicOptimiser:
        def __init__(
            self,
            components,
            budget=None,
            target_gain=None,
            allow_slack=False,
            verbose=False,
        ):
            captured["target_gain"] = target_gain
            self.solution = []
            self.solution_cost = 0.0
            self.solution_gain = 0.0

        def solve(self):
            pass

    monkeypatch.setattr(
        "recommendations.optimiser.funding_optimiser.StrategicOptimiser",
        FakeStrategicOptimiser,
    )

    run_optimizer(
        [[{"cost": 100, "gain": 5}]],
        budget=500,
        sub_target_gain=0,
    )

    assert captured["target_gain"] == 0


def test_sub_target_gain_none_becomes_infinity(monkeypatch):
    captured = {}

    class FakeStrategicOptimiser:
        def __init__(
            self,
            components,
            budget=None,
            target_gain=None,
            allow_slack=False,
            verbose=False,
        ):
            captured["target_gain"] = target_gain
            self.solution = []
            self.solution_cost = 0.0
            self.solution_gain = 0.0

        def solve(self):
            pass

    monkeypatch.setattr(
        "recommendations.optimiser.funding_optimiser.StrategicOptimiser",
        FakeStrategicOptimiser,
    )

    run_optimizer(
        [[{"cost": 100, "gain": 5}]],
        budget=500,
        sub_target_gain=None,
    )

    assert captured["target_gain"] == None


def test_target_only_case(monkeypatch):
    captured = {}

    class FakeStrategicOptimiser:
        def __init__(
            self,
            components,
            budget=None,
            target_gain=None,
            allow_slack=False,
            verbose=False,
        ):
            captured["budget"] = budget
            captured["target_gain"] = target_gain

            self.solution = [{"cost": 50, "gain": 10}]
            self.solution_cost = 50
            self.solution_gain = 10

        def solve(self):
            pass

    monkeypatch.setattr(
        "recommendations.optimiser.funding_optimiser.StrategicOptimiser",
        FakeStrategicOptimiser,
    )

    solution, cost, gain = run_optimizer(
        [[{"cost": 50, "gain": 10}]],
        sub_target_gain=10,
    )

    assert captured["budget"] is None
    assert captured["target_gain"] == 10

    assert cost == 50
    assert gain == 10
    assert solution == [{"cost": 50, "gain": 10}]
