# Copyright (c) 2020 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

from datetime import datetime
from decimal import Decimal
from typing import NamedTuple, Optional
from uuid import UUID

import pytest
from dateutil.tz import tzutc
from pydantic import BaseModel

from czc import json
from czt.unittest import MATCH


class SomeModel(BaseModel):
    date_val: Optional[datetime]


class SomeTuple(NamedTuple):
    some_val: int
    other_val: str


class SomeClass:
    def __init__(self, some_val, other_val):
        self.some_val = some_val
        self.other_val = other_val

    some_val: int
    other_val: str

    def serializable(self):
        return {'type': str(self.__class__), **self.__dict__}


@pytest.mark.unit
@pytest.mark.parametrize(
    'value,expected_value',
    [
        (Decimal('123.4'), '123.4'),
        (UUID('{12345678-1234-5678-1234-567812345678}'), '12345678-1234-5678-1234-567812345678'),
        (datetime(2021, 3, 10, 15, 45, 12, tzinfo=tzutc()), '2021-03-10T15:45:12+00:00'),
        ({'a', 'b', 'c'}, MATCH(lambda x: sorted(x) == ['a', 'b', 'c'])),
        (SomeTuple(some_val=123, other_val='A nice message.'), {'some_val': 123, 'other_val': 'A nice message.'}),
        (
            SomeClass(some_val=123, other_val='A nice message.'),
            {'type': "<class 'tests.test_json.SomeClass'>", 'some_val': 123, 'other_val': 'A nice message.'},
        ),
    ],
)
def test_extended_encoder_should_handle_extended_types(value, expected_value):
    root = {
        'root_data1': {'x': 1, 'y': 2},
        'root_data2': ['a', 'b'],
    }
    result = json.dumps({**root, 'test': value})
    assert json.loads(result) == {**root, 'test': expected_value}


@pytest.mark.unit
def test_dumps_handles_pydantic_models():
    # given
    obj = {'date_val': '2021-03-10T15:45:12+00:00'}
    model = SomeModel.model_validate(obj)
    # when
    result = json.dumps(model)
    # then
    assert json.loads(result) == obj


@pytest.mark.unit
def test_dumps_handles_pydantic_models_extra_kwargs():
    # given
    model = SomeModel(date_val=None)
    # when (exclude_none=True is a BaseModel.json() kwarg)
    result = json.dumps(model, exclude_none=True)
    # then
    assert json.loads(result) == {}


@pytest.mark.unit
def test_serializable_handles_pydantic_models():
    # given
    obj = {'date_val': '2021-03-10T15:45:12+00:00'}
    model = SomeModel.model_validate(obj)
    # when
    result = json.serializable(model)
    # then
    assert result == obj
