
# Copyright (C) 2010-2023 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/

import io
import datetime
import decimal
import textwrap

import pytest

from rhodecode.lib import ext_json
from rhodecode.translation import _, _pluralize


class Timezone(datetime.tzinfo):
    def __init__(self, hours):
        self.hours = hours

    def utcoffset(self, unused_dt):
        return datetime.timedelta(hours=self.hours)


class SerializableObject(object):
    def __json__(self):
        return 'foo'


def test_dumps_set():
    result = ext_json.json.dumps(set((1, 2, 3)))
    # We cannot infer what the order of result is going to be
    result = ext_json.json.loads(result)
    assert isinstance(result, list)
    assert [1, 2, 3] == sorted(result)


def test_dumps_decimal():
    assert b'"1.5"' == ext_json.json.dumps(decimal.Decimal('1.5'))


def test_dumps_complex():
    assert b"[0.0,1.0]" == ext_json.json.dumps(1j)
    assert b"[1.0,0.0]" == ext_json.json.dumps(1 + 0j)
    assert b"[1.1,1.2]" == ext_json.json.dumps(1.1 + 1.2j)


def test_dumps_object_with_json_method():
    assert '"foo"' == ext_json.str_json(SerializableObject())


def test_dumps_object_with_json_attribute():

    assert '"foo"' == ext_json.str_json(SerializableObject())


def test_dumps_time():
    assert '"03:14:15.926535"' == ext_json.str_json(datetime.time(3, 14, 15, 926535))


def test_dumps_time_no_microseconds():
    assert '"03:14:15"' == ext_json.str_json(datetime.time(3, 14, 15))


def test_dumps_time_with_timezone():
    with pytest.raises(TypeError) as excinfo:
        ext_json.json.dumps(datetime.time(3, 14, 15, 926535, Timezone(0)))

    error_msg = str(excinfo.value)

    assert 'timezone library is not supported' in error_msg
    # only for simplejson
    #assert 'Time-zone aware times are not JSON serializable' in error_msg


def test_dumps_date():
    assert b'"1969-07-20"' == ext_json.json.dumps(datetime.date(1969, 7, 20))


def test_dumps_datetime():
    json_data = ext_json.json.dumps(datetime.datetime(1969, 7, 20, 3, 14, 15, 926535))
    assert b'"1969-07-20T03:14:15.926535"' == json_data


def test_dumps_datetime_no_microseconds():
    json_data = ext_json.json.dumps(datetime.datetime(1969, 7, 20, 3, 14, 15))
    assert b'"1969-07-20T03:14:15"' == json_data


def test_dumps_datetime_with_utc_timezone():
    json_data = ext_json.json.dumps(
        datetime.datetime(1969, 7, 20, 3, 14, 15, 926535, Timezone(0)))
    assert b'"1969-07-20T03:14:15.926535+00:00"' == json_data


def test_dumps_datetime_with_plus1_timezone():
    json_data = ext_json.json.dumps(
        datetime.datetime(1969, 7, 20, 3, 14, 15, 926535, Timezone(1)))
    assert b'"1969-07-20T03:14:15.926535+01:00"' == json_data


def test_dumps_unserializable_class():
    unserializable_obj = object()
    with pytest.raises(TypeError) as excinfo:
        ext_json.json.dumps(unserializable_obj)

    assert 'object' in str(excinfo.value)
    assert 'is not JSON serializable' in str(excinfo.value)


def test_dump_is_like_dumps():
    data = {
        'decimal': decimal.Decimal('1.5'),
        'set': set([1]),  # Just one element to guarantee the order
        'complex': 1 - 1j,
        'datetime': datetime.datetime(1969, 7, 20, 3, 14, 15, 926535),
        'time': datetime.time(3, 14, 15, 926535),
        'date': datetime.date(1969, 7, 20),
    }
    json_buffer = io.StringIO()  # StringIO because dump uses simplejson not orjson
    ext_json.json.dump(data, json_buffer)

    assert ext_json.sjson.dumps(data) == json_buffer.getvalue()


def test_formatted_json():
    data = {
        'b': {'2': 2, '1': 1},
        'a': {'3': 3, '4': 4},
    }

    expected_data = textwrap.dedent('''
    {
      "a": {
        "3": 3,
        "4": 4
      },
      "b": {
        "1": 1,
        "2": 2
      }
    }''').strip()

    assert expected_data == ext_json.formatted_str_json(data)


def test_lazy_translation_string(baseapp):
    data = {'label': _('hello')}
    data2 = {'label2': _pluralize('singular', 'plural', 1)}

    assert b'{"label":"hello"}' == ext_json.json.dumps(data)
    assert b'{"label2":"singular"}' == ext_json.json.dumps(data2)
