# imports

from src.topupopt.problems.esipp.resource import ResourcePrice
from src.topupopt.problems.esipp.resource import are_prices_time_invariant

# *****************************************************************************
# *****************************************************************************


class TestResourcePrice:
    # *************************************************************************
    # *************************************************************************

    def test_resources_time_invariant(self):
        # single entry

        resource_prices = {
            (0, 0, 0): ResourcePrice(prices=1, volumes=None),
        }

        assert are_prices_time_invariant(resource_prices)

        # *********************************************************************

        # single assessment, two periods, same prices for both periods

        resource_prices = {
            (0, 0, 0): ResourcePrice(prices=1, volumes=None),
            (0, 0, 1): ResourcePrice(prices=1, volumes=None),
            (0, 0, 2): ResourcePrice(prices=1, volumes=None),
            (0, 1, 0): ResourcePrice(prices=1, volumes=None),
            (0, 1, 1): ResourcePrice(prices=1, volumes=None),
            (0, 1, 2): ResourcePrice(prices=1, volumes=None),
        }

        assert are_prices_time_invariant(resource_prices)

        # *********************************************************************

        # single assessment, two periods, same prices per period

        resource_prices = {
            (0, 0, 0): ResourcePrice(prices=1, volumes=None),
            (0, 0, 1): ResourcePrice(prices=1, volumes=None),
            (0, 0, 2): ResourcePrice(prices=1, volumes=None),
            (0, 1, 0): ResourcePrice(prices=2, volumes=None),
            (0, 1, 1): ResourcePrice(prices=2, volumes=None),
            (0, 1, 2): ResourcePrice(prices=2, volumes=None),
        }

        assert are_prices_time_invariant(resource_prices)

        # *********************************************************************

        # single assessment, two periods, different prices in a given period

        resource_prices = {
            (0, 0, 0): ResourcePrice(prices=1, volumes=None),
            (0, 0, 1): ResourcePrice(prices=1, volumes=None),
            (0, 0, 2): ResourcePrice(prices=1, volumes=None),
            (0, 1, 0): ResourcePrice(prices=2, volumes=None),
            (0, 1, 1): ResourcePrice(prices=2.5, volumes=None),
            (0, 1, 2): ResourcePrice(prices=2, volumes=None),
        }

        assert not are_prices_time_invariant(resource_prices)

        # *********************************************************************

    # *************************************************************************
    # *************************************************************************

    def test_resource_prices_reals(self):
        # 1) single segment, no volume limit, real input

        prices = 3

        volumes = None

        res_p = ResourcePrice(prices=prices, volumes=volumes)

        assert res_p.number_segments(redo=False) == 1

        assert res_p.number_segments(redo=True) == 1

        assert not res_p.is_volume_capped()

        assert res_p.price_monotonically_increasing_with_volume()

        assert res_p.price_monotonically_decreasing_with_volume()

        assert res_p.is_volume_invariant()

        # 2) single segment, volume limit, real input

        prices = 3

        volumes = 1.5

        res_p = ResourcePrice(prices=prices, volumes=volumes)

        assert res_p.number_segments(redo=False) == 1

        assert res_p.number_segments(redo=True) == 1

        assert res_p.is_volume_capped()

        assert res_p.price_monotonically_increasing_with_volume()

        assert res_p.price_monotonically_decreasing_with_volume()

        assert res_p.is_volume_invariant()

    # *************************************************************************
    # *************************************************************************

    def test_equivalence_single(self):
        # *********************************************************************
        # *********************************************************************

        # single segment

        # *********************************************************************
        # *********************************************************************

        # no volume limit

        # single segment, no volume limit, different formats
        # prices and volumes match =  True

        prices = 3
        volumes = None
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices], volumes=[volumes])
        assert res_p1 == res_p2
        assert res_p2 == res_p1

        # *********************************************************************

        # single segment, no volume limit, different formats
        # prices do not match = False

        prices = 3
        volumes = None
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices + 1], volumes=[volumes])
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************

        # single segment, no volume limit, same format
        # prices and volumes match =  True

        prices = 3
        volumes = None
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert res_p1 == res_p2
        assert res_p2 == res_p1

        # *********************************************************************

        # single segment, no volume limit, same format
        # prices do not match = False

        prices = 3
        volumes = None
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices + 1, volumes=volumes)
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************
        # *********************************************************************

        # with volume limits

        # single segment, volume limit, different formats
        # prices and volumes match =  True

        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices], volumes=[volumes])
        assert res_p1 == res_p2
        assert res_p2 == res_p1

        # *********************************************************************

        # single segment, volume limit, different formats: False
        # prices do not match = False

        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices + 1], volumes=[volumes])
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************

        # single segment, volume limit, same format
        # prices and volumes match =  True

        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert res_p1 == res_p2
        assert res_p2 == res_p1

        # *********************************************************************

        # single segment, volume limit, same format: False
        # prices do not match = False

        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices + 1, volumes=volumes)
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************

        # single segment, volume limit, different formats
        # volumes do not match = False

        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices], volumes=[volumes + 1])
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************

        # single segment, volume limit, same format
        # volumes do not match = False

        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=volumes + 1)
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************

        # single segment, volume limit, different formats
        # volumes do not match = False

        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=[prices], volumes=[None])
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************

        # single segment, volume limit, same format
        # volumes do not match = False

        prices = 3
        volumes = 1
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=None)
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************
        # *********************************************************************

    # *************************************************************************
    # *************************************************************************

    def test_equivalence_multiple_segments(self):
        # *********************************************************************
        # *********************************************************************

        # multiple segments

        # *********************************************************************
        # *********************************************************************

        # no volume limit

        # two segments, no volume limit, same format
        # prices and volumes match =  True

        prices = [1, 3]
        volumes = [1, None]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert res_p1 == res_p2
        assert res_p2 == res_p1

        # two segments, no volume limit, same format
        # prices do not match = False

        prices = [1, 3]
        volumes = [1, None]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        prices = [2, 3]
        volumes = [1, None]
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************

        # with volume limits

        # two segments segment, volume limit, same format
        # prices and volumes match =  True

        prices = [1, 3]
        volumes = [1, 3]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert res_p1 == res_p2
        assert res_p2 == res_p1

        # two segments, volume limit, same format: False
        # prices do not match = False

        prices = [1, 3]
        volumes = [1, 4]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        prices = [1, 4]
        volumes = [1, 4]
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************

        # volumes do not match

        # single segment, volume limit, same format
        # volumes do not match = False

        prices = [1, 3]
        volumes = [1, 4]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        prices = [1, 3]
        volumes = [1, 5]
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # single segment, volume limit, same format
        # volumes do not match = False

        prices = [1, 3]
        volumes = [1, 4]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        prices = [1, 3]
        volumes = [1, None]
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************
        # *********************************************************************

        # different number of segments

        prices = [1, 3]
        volumes = [1, 4]
        res_p1 = ResourcePrice(prices=prices, volumes=volumes)
        prices = [1, 3, 5]
        volumes = [1, 4, None]
        res_p2 = ResourcePrice(prices=prices, volumes=volumes)
        assert not res_p1 == res_p2
        assert not res_p2 == res_p1

        # *********************************************************************
        # *********************************************************************

    # *************************************************************************
    # *************************************************************************

    def test_resource_prices_lists(self):
        # *********************************************************************

        # aspects that were tested:
        # i) number of segments (1, multiple or none)
        # ii) price variations (increasing, decreasing and stable)
        # iii) volume limits

        # *********************************************************************

        # 1) multiple segments, prices increase, volume limits
        # 2) multiple segments, prices decrease, volume limits
        # 3) multiple segments, prices are stable, volume limits
        # 4) multiple segments, prices increase, no volume limit
        # 5) multiple segments, prices decrease, no volume limit
        # 6) multiple segments, prices are stable, no volume limit
        # 7) one segment, prices are stable, volume limits
        # 8) one segment, prices are stable, no volume limit

        # *********************************************************************

        # 1) multiple segments, prices increase, volume limits

        prices = [1, 2, 3]

        volumes = [2, 1, 3]

        res_p = ResourcePrice(prices=prices, volumes=volumes)

        assert res_p.number_segments(redo=True) == 3

        assert res_p.number_segments(redo=False) == 3

        assert res_p.is_volume_capped()

        assert res_p.price_monotonically_increasing_with_volume()

        assert not res_p.price_monotonically_decreasing_with_volume()

        assert not res_p.is_volume_invariant()

        # *********************************************************************

        # 2) multiple segments, prices decrease, volume limits

        prices = [3, 2, 1]

        volumes = [2, 1, 3]

        res_p = ResourcePrice(prices=prices, volumes=volumes)

        assert res_p.number_segments(redo=False) == 3

        assert res_p.is_volume_capped()

        assert not res_p.price_monotonically_increasing_with_volume()

        assert res_p.price_monotonically_decreasing_with_volume()

        assert not res_p.is_volume_invariant()

        # *********************************************************************

        # 3) multiple segments, prices are stable, volume limits

        prices = [2, 2, 2]

        volumes = [2, 1, 3]

        res_p = ResourcePrice(prices=prices, volumes=volumes)

        assert res_p.number_segments(redo=True) == 3

        assert res_p.number_segments(redo=False) == 3

        assert res_p.is_volume_capped()

        assert res_p.price_monotonically_increasing_with_volume()

        assert res_p.price_monotonically_decreasing_with_volume()

        assert res_p.is_volume_invariant()

        # *********************************************************************

        # 4) multiple segments, prices increase, no volume limit

        prices = [1, 2, 3]

        volumes = [2, 1, None]

        res_p = ResourcePrice(prices=prices, volumes=volumes)

        assert res_p.number_segments(redo=True) == 3

        assert res_p.number_segments(redo=False) == 3

        assert not res_p.is_volume_capped()

        assert res_p.price_monotonically_increasing_with_volume()

        assert not res_p.price_monotonically_decreasing_with_volume()

        assert not res_p.is_volume_invariant()

        # *********************************************************************

        # 5) multiple segments, prices decrease, no volume limit

        prices = [2, 2, 2]

        volumes = [2, 1, None]

        res_p = ResourcePrice(prices=prices, volumes=volumes)

        assert res_p.number_segments(redo=True) == 3

        assert res_p.number_segments(redo=False) == 3

        assert not res_p.is_volume_capped()

        assert res_p.price_monotonically_increasing_with_volume()

        assert res_p.price_monotonically_decreasing_with_volume()

        assert res_p.is_volume_invariant()

        # *********************************************************************

        # 6) multiple segments, prices are stable, no volume limit

        prices = [2, 2, 2]

        volumes = [2, 1, None]

        res_p = ResourcePrice(prices=prices, volumes=volumes)

        assert res_p.number_segments(redo=True) == 3

        assert res_p.number_segments(redo=False) == 3

        assert not res_p.is_volume_capped()

        assert res_p.price_monotonically_increasing_with_volume()

        assert res_p.price_monotonically_decreasing_with_volume()

        assert res_p.is_volume_invariant()

        # *********************************************************************

        # 7) one segment, prices are stable, volume limits

        prices = [2]

        volumes = [2]

        res_p = ResourcePrice(prices=prices, volumes=volumes)

        assert res_p.number_segments(redo=True) == 1

        assert res_p.number_segments(redo=False) == 1

        assert res_p.is_volume_capped()

        assert res_p.price_monotonically_increasing_with_volume()

        assert res_p.price_monotonically_decreasing_with_volume()

        assert res_p.is_volume_invariant()

        # *********************************************************************

        # 8) one segment, prices are stable, no volume limit

        prices = [3]

        volumes = [None]

        res_p = ResourcePrice(prices=prices, volumes=volumes)

        assert res_p.number_segments(redo=True) == 1

        assert res_p.number_segments(redo=False) == 1

        assert not res_p.is_volume_capped()

        assert res_p.price_monotonically_increasing_with_volume()

        assert res_p.price_monotonically_decreasing_with_volume()

        assert res_p.is_volume_invariant()

        res_p = ResourcePrice(prices=prices[0], volumes=volumes[0])

        assert res_p.number_segments(redo=True) == 1

        assert res_p.number_segments(redo=False) == 1

        assert not res_p.is_volume_capped()

        assert res_p.price_monotonically_increasing_with_volume()

        assert res_p.price_monotonically_decreasing_with_volume()

        assert res_p.is_volume_invariant()

        # *********************************************************************

        # errors

        # *********************************************************************

        # create object without prices

        error_raised = False

        try:
            _ = ResourcePrice(prices=None, volumes=volumes)
        except TypeError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object with negative prices in lists

        error_raised = False

        try:
            _ = ResourcePrice(prices=[7, -3, 2], volumes=[3, 4, 5])
        except ValueError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object where an intermediate segment has no volume limit

        error_raised = False

        try:
            _ = ResourcePrice(prices=[7, 4, 2], volumes=[3, None, 5])
        except ValueError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object with negative volumes in lists

        error_raised = False

        try:
            _ = ResourcePrice(prices=[7, 3, 2], volumes=[4, -1, 2])
        except ValueError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object with non-numeric prices in lists

        error_raised = False

        try:
            _ = ResourcePrice(prices=[7, "4", 2], volumes=[3, 4, 5])
        except TypeError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object with non-numeric volumes in lists

        error_raised = False

        try:
            _ = ResourcePrice(prices=[7, 3, 2], volumes=[4, "3", 2])
        except TypeError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object with mismatched price and volume lists

        error_raised = False

        try:
            _ = ResourcePrice(prices=[7, 3, 2], volumes=[5, 7])
        except ValueError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object with a price list as an input and an unsupported type

        error_raised = False

        try:
            _ = ResourcePrice(prices=[7, 3, 2], volumes="hello")
        except TypeError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object with negative prices in lists (no volumes are provided)

        error_raised = False

        try:
            _ = ResourcePrice(prices=[7, 3, -2], volumes=None)
        except TypeError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object with non-numeric prices in lists (no volumes are provided)

        error_raised = False

        try:
            _ = ResourcePrice(prices=[7, 3, "a"], volumes=None)
        except TypeError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object with non-numeric prices in lists (no volumes are provided)

        error_raised = False

        try:
            _ = ResourcePrice(prices=5, volumes=[7, 3, 4])
        except TypeError:
            error_raised = True
        assert error_raised

        # *********************************************************************

        # create object with negative prices

        error_raised = False

        try:
            _ = ResourcePrice(prices=-3, volumes=None)
        except ValueError:
            error_raised = True
        assert error_raised

        # *********************************************************************


# *****************************************************************************
# *****************************************************************************