# Copyright (c) 2023 CloudZero - ALL RIGHTS RESERVED - PROPRIETARY AND CONFIDENTIAL
# Unauthorized copying of this file and/or project, via any medium is strictly prohibited.
# Direct all questions to legal@cloudzero.com

import pytest

from czc.exceptions import ValuesHasMultipleDispatchesError, InvalidValueError
from czc.value_dispatch import value_dispatch


@value_dispatch(str)
def perform_operation_str(value: str, value_a, value_b):
    return f'{value}: {value_a} {value_b}'


@perform_operation_str.register('add')
def _add(value_a, value_b):
    return value_a + value_b


@perform_operation_str.register('subtract')
def _subtract(value_a, value_b):
    return value_a - value_b


@perform_operation_str.register('multiply')
def _multiply(value_a, value_b):
    return value_a * value_b


@value_dispatch(int)
def perform_operation_int(value: int, value_a):
    return f'{value}: {value_a}'


@perform_operation_int.register(10)
def _multiply_by_10(value_a):
    return value_a * 10


@perform_operation_int.register(11)
def _multiply_by_11(value_a):
    return value_a * 11


@pytest.mark.unit
@pytest.mark.parametrize('operation, a, b, expected_result', [
    ('add', 5, 3, 8),
    ('subtract', 5.3, 3.4, 1.9),
    ('multiply', 5, 3, 15),
    pytest.param('default', 5, 3, 'default: 5 3', id='default-str-handler')
])
def test_str_value_dispatch(operation: str, a, b, expected_result):
    assert expected_result == perform_operation_str(operation, a, b)


@pytest.mark.unit
@pytest.mark.parametrize('operation, a, expected_result', [
    (10, 3, 30),
    (11, 3, 33),
    pytest.param(5, 3, '5: 3', id='default-int-handler')
])
def test_int_value_dispatch(operation: str, a, expected_result):
    assert expected_result == perform_operation_int(operation, a)


@pytest.mark.unit
def test_value_dispatch_catches_overlaps():
    with pytest.raises(ValuesHasMultipleDispatchesError):
        @perform_operation_str.register('add')
        def _another_add(a, b):
            return b + a


@pytest.mark.unit
def test_value_dispatch_catches_registering_invalid_type():
    with pytest.raises(InvalidValueError):
        @perform_operation_str.register(0)
        def _another_add(a, b):
            return b + a


@pytest.mark.unit
def test_value_dispatch_catches_dispatch_of_invalid_enum():
    with pytest.raises(InvalidValueError):
        perform_operation_str(0, 5, 3)  # ignore: type
