import json

import pytest
from ocdsextensionregistry import ProfileBuilder
from ocdsmerge.exceptions import DuplicateIdValueWarning, InconsistentTypeError

from ocdskit.combine import merge, package_records
from ocdskit.exceptions import (
    InconsistentVersionError,
    MergeErrorWarning,
    UnknownVersionError,
)
from tests import read

inconsistent = [
    {"ocid": "ocds-213czf-1", "date": "2000-01-01T00:00:00Z", "integer": 1},
    {"ocid": "ocds-213czf-1", "date": "2000-01-02T00:00:00Z", "integer": {"object": 1}},
]


def test_package_default_arguments():
    data = [
        item
        for filename in ("realdata/record-package-1.json", "realdata/record-package-2.json")
        for item in json.loads(read(filename))["records"]
    ]

    actual = package_records(data)

    assert actual == json.loads(read("realdata/record-package_record-package.json"))


def test_merge_empty():
    compiled_releases = list(merge([]))

    assert compiled_releases == []


def test_merge_with_schema():
    builder = ProfileBuilder("1__1__4", {"additionalContactPoint": "master"})
    schema = builder.patched_release_schema()

    data = json.loads(read("release-package_additional-contact-points.json"))["releases"]
    compiled_release = next(iter(merge(data, schema=schema)))

    assert compiled_release == json.loads(read("compile_extensions.json"))


def test_merge_without_schema():
    data = json.loads(read("release-package_additional-contact-points.json"))["releases"]
    compiled_release = next(iter(merge(data)))

    assert compiled_release == json.loads(read("compile_no-extensions.json"))


def test_merge_warning():
    data = json.loads(read("release-package_warning.json"))["releases"]

    with pytest.warns(DuplicateIdValueWarning) as records:
        compiled_release = next(iter(merge(data)))

    assert compiled_release == json.loads(read("compile_warning.json"))

    assert [record.message for record in records] == [
        ("ocds-213czf-1: Multiple objects have the `id` value '1' in the `parties` array"),
    ]


def test_merge_unknown_version(capsys, monkeypatch, caplog):
    def data():
        yield json.loads(read("release-package_unknown-version.json"))

    with pytest.raises(UnknownVersionError):
        list(merge(data()))


def test_merge_unknown_version_force_version(capsys, monkeypatch, caplog):
    def data():
        yield json.loads(read("release-package_unknown-version.json"))

    list(merge(data(), force_version="1.1", ignore_version=True))  # no error


def test_merge_version_mismatch():
    def data():
        yield json.loads(read("realdata/release-package_1.1-1.json"))
        yield json.loads(read("realdata/release-package_1.0-1.json"))

    with pytest.raises(InconsistentVersionError):
        list(merge(data()))


def test_merge_version_mismatch_ignore_version():
    def data():
        yield json.loads(read("realdata/release-package_1.1-1.json"))
        yield json.loads(read("realdata/release-package_1.0-1.json"))

    list(merge(data(), ignore_version=True))  # no error


@pytest.mark.parametrize("return_package", [True, False])
def test_merge_inconsistent_type(return_package):
    def data():
        for release in inconsistent:
            yield {"releases": [release]}

    with pytest.raises(InconsistentTypeError):
        list(merge(data(), return_package=return_package))


@pytest.mark.parametrize(
    ("return_package", "expected"),
    [
        (
            True,
            [
                {
                    "uri": "",
                    "publisher": {},
                    "publishedDate": "",
                    "version": "1.1",
                    "records": [{"ocid": "ocds-213czf-1", "releases": inconsistent}],
                },
            ],
        ),
        (
            False,
            [],
        ),
    ],
)
def test_merge_inconsistent_type_convert_exceptions_to_warnings(return_package, expected):
    def data():
        for release in inconsistent:
            yield {"releases": [release]}

    with pytest.warns(MergeErrorWarning) as records:
        output = list(merge(data(), return_package=return_package, convert_exceptions_to_warnings=True))

    assert output == expected
    assert len(records) == 1
    assert (
        str(records[0].message)
        == "ocds-213czf-1: An earlier release had the value 1 for /integer, but the current release has an object with a 'object' key"  # noqa: E501
    )
