from itertools import combinations

import pytest

from asdf.versioning import (
    AsdfVersion,
    AsdfSpec,
    supported_versions,
    get_version_map,
    join_tag_version,
)
from asdf.extension import default_extensions
from asdf.schema import load_schema


def test_version_constructor():
    ver0 = AsdfVersion('1.0.0')
    ver1 = AsdfVersion((1,0,0))
    ver2 = AsdfVersion([1,0,0])

    assert str(ver0) == '1.0.0'
    assert str(ver1) == '1.0.0'
    assert str(ver2) == '1.0.0'

def test_version_and_version_equality():
    ver0 = AsdfVersion('1.0.0')
    ver1 = AsdfVersion('1.0.0')

    assert ver0 is not ver1
    assert ver0 == ver1
    assert ver1 == ver0
    assert not (ver0 != ver1)
    assert not (ver1 != ver0)

def test_version_and_string_equality():
    version = AsdfVersion('1.0.0')
    string_ver = '1.0.0'

    assert version == string_ver
    assert string_ver == version
    assert not (version != string_ver)
    assert not (string_ver != version)

def test_version_and_tuple_equality():
    version = AsdfVersion('1.0.0')
    tuple_ver = (1,0,0)

    assert version == tuple_ver
    assert tuple_ver == version
    assert not (version != tuple_ver)
    assert not (tuple_ver != version)

def test_version_and_version_inequality():
    ver0 = AsdfVersion('1.0.0')
    ver1 = AsdfVersion('1.0.1')
    ver2 = AsdfVersion('1.1.0')
    ver3 = AsdfVersion('1.1.1')
    ver4 = AsdfVersion('2.0.0')
    ver5 = AsdfVersion('2.0.1')
    ver6 = AsdfVersion('2.1.0')
    ver7 = AsdfVersion('2.1.1')

    versions = [ver0, ver1, ver2, ver3, ver4, ver5, ver6, ver7]
    for x,y in combinations(versions, 2):
        assert not (x == y)
        assert x != y

    assert ver0 < ver1 < ver2 < ver3 < ver4 < ver5 < ver6 < ver7
    assert ver7 > ver6 > ver5 > ver4 > ver3 > ver2 > ver1 > ver0
    assert (ver0 < ver1 < ver2 < ver4 < ver3 < ver5 < ver6 < ver7) == False
    assert (ver7 > ver6 > ver5 > ver3 > ver4 > ver2 > ver1 > ver0) == False

    assert ver0 <= ver1 <= ver2 <= ver3 <= ver4 <= ver5 <= ver6 <= ver7
    assert ver7 >= ver6 >= ver5 >= ver4 >= ver3 >= ver2 >= ver1 >= ver0

def test_version_and_string_inequality():
    version = AsdfVersion('2.0.0')

    assert version > '1.0.0'
    assert version > '1.0.1'
    assert version > '1.1.0'
    assert version > '1.1.1'
    assert (version > '2.0.0') == False
    assert (version < '2.0.0') == False
    assert version < '2.0.1'
    assert version < '2.1.0'
    assert version < '2.1.1'

    assert version >= '1.0.0'
    assert version >= '1.0.1'
    assert version >= '1.1.0'
    assert version >= '1.1.1'
    assert version >= '2.0.0'
    assert version <= '2.0.0'
    assert version <= '2.0.1'
    assert version <= '2.1.0'
    assert version <= '2.1.1'

    assert '1.0.0' < version
    assert '1.0.1' < version
    assert '1.1.0' < version
    assert '1.1.1' < version
    assert ('2.0.0' < version) == False
    assert ('2.0.0' > version) == False
    assert '2.0.1' > version
    assert '2.1.0' > version
    assert '2.1.1' > version

    assert '1.0.0' <= version
    assert '1.0.1' <= version
    assert '1.1.0' <= version
    assert '1.1.1' <= version
    assert '2.0.0' <= version
    assert '2.0.0' >= version
    assert '2.0.1' >= version
    assert '2.1.0' >= version
    assert '2.1.1' >= version


def test_version_and_tuple_inequality():
    version = AsdfVersion('2.0.0')

    assert version > (1,0,0)
    assert version > (1,0,1)
    assert version > (1,1,0)
    assert version > (1,1,1)
    assert (version > (2,0,0)) == False
    assert (version < (2,0,0)) == False
    assert version < (2,0,1)
    assert version < (2,1,0)
    assert version < (2,1,1)

    assert version >= (1,0,0)
    assert version >= (1,0,1)
    assert version >= (1,1,0)
    assert version >= (1,1,1)
    assert version >= (2,0,0)
    assert version <= (2,0,0)
    assert version <= (2,0,1)
    assert version <= (2,1,0)
    assert version <= (2,1,1)

    assert (1,0,0) < version
    assert (1,0,1) < version
    assert (1,1,0) < version
    assert (1,1,1) < version
    assert ((2,0,0) < version) == False
    assert ((2,0,0) > version) == False
    assert (2,0,1) > version
    assert (2,1,0) > version
    assert (2,1,1) > version

    assert (1,0,0) <= version
    assert (1,0,1) <= version
    assert (1,1,0) <= version
    assert (1,1,1) <= version
    assert (2,0,0) <= version
    assert (2,0,0) >= version
    assert (2,0,1) >= version
    assert (2,1,0) >= version
    assert (2,1,1) >= version

def test_spec_version_match():
    spec = AsdfSpec('>=1.1.0')

    assert spec.match(AsdfVersion('1.1.0'))
    assert spec.match(AsdfVersion('1.2.0'))
    assert not spec.match(AsdfVersion('1.0.0'))
    assert not spec.match(AsdfVersion('1.0.9'))

def test_spec_version_select():
    spec = AsdfSpec('>=1.1.0')

    versions = [AsdfVersion(x) for x in ['1.0.0', '1.0.9', '1.1.0', '1.2.0']]
    assert spec.select(versions) == '1.2.0'
    assert spec.select(versions[:-1]) == '1.1.0'
    assert spec.select(versions[:-2]) == None

def test_spec_version_filter():
    spec = AsdfSpec('>=1.1.0')

    versions = [AsdfVersion(x) for x in ['1.0.0', '1.0.9', '1.1.0', '1.2.0']]
    for x,y in zip(spec.filter(versions), ['1.1.0', '1.2.0']):
        assert x == y

def test_spec_string_match():
    spec = AsdfSpec('>=1.1.0')

    assert spec.match('1.1.0')
    assert spec.match('1.2.0')
    assert not spec.match('1.0.0')
    assert not spec.match('1.0.9')

def test_spec_string_select():
    spec = AsdfSpec('>=1.1.0')

    versions = ['1.0.0', '1.0.9', '1.1.0', '1.2.0']
    assert spec.select(versions) == '1.2.0'
    assert spec.select(versions[:-1]) == '1.1.0'
    assert spec.select(versions[:-2]) == None

def test_spec_string_filter():
    spec = AsdfSpec('>=1.1.0')

    versions = ['1.0.0', '1.0.9', '1.1.0', '1.2.0']
    for x,y in zip(spec.filter(versions), ['1.1.0', '1.2.0']):
        assert x == y

def test_spec_tuple_match():
    spec = AsdfSpec('>=1.1.0')

    assert spec.match((1,1,0))
    assert spec.match((1,2,0))
    assert not spec.match((1,0,0))
    assert not spec.match((1,0,9))

def test_spec_tuple_select():
    spec = AsdfSpec('>=1.1.0')

    versions = [(1,0,0), (1,0,9), (1,1,0), (1,2,0)]
    assert spec.select(versions) == '1.2.0'
    assert spec.select(versions[:-1]) == '1.1.0'
    assert spec.select(versions[:-2]) == None

def test_spec_tuple_filter():
    spec = AsdfSpec('>=1.1.0')

    versions = [(1,0,0), (1,0,9), (1,1,0), (1,2,0)]
    for x,y in zip(spec.filter(versions), ['1.1.0', '1.2.0']):
        assert x == y

def test_spec_equal():
    """Make sure that equality means match"""
    spec = AsdfSpec('>=1.2.0')
    version0 = AsdfVersion('1.1.0')
    version1 = AsdfVersion('1.3.0')

    assert spec != version0
    assert version0 != spec
    assert spec == version1
    assert version1 == spec

    assert spec != '1.1.0'
    assert '1.1.0' != spec
    assert spec == '1.3.0'
    assert '1.3.0' == spec

    assert spec != (1, 1, 0)
    assert (1, 1, 0) != spec
    assert spec == (1, 3, 0)
    assert (1, 3, 0) == spec


@pytest.mark.parametrize("version", supported_versions)
def test_version_map_core_support(version):
    _test_version_map_support(version, "core")


@pytest.mark.parametrize("version", supported_versions)
@pytest.mark.xfail(
    reason="astropy does not yet explicitly support older schema versions",
    strict=True
)
def test_version_map_standard_support(version):
    _test_version_map_support(version, "standard")


def _test_version_map_support(version, schema_type):
    vm = get_version_map(version)

    type_index = default_extensions.extension_list.type_index
    class MockContext:
        def __init__(self):
            self._fname = None
    ctx = MockContext()

    for tag_base, tag_version in vm[schema_type].items():
        tag = join_tag_version(tag_base, tag_version)

        try:
            load_schema(tag)
        except Exception:
            assert False, (
                "ASDF Standard version {} requires support for ".format(version) +
                "{}, but the corresponding schema cannot be loaded.".format(tag)
            )

        extension_type = type_index.from_yaml_tag(ctx, tag)
        assert extension_type is not None, (
            "ASDF Standard version {} requires support for ".format(version) +
            "{}, but no ExtensionType exists to support that tag.".format(tag)
        )

        assert extension_type.yaml_tag == tag, (
            "ASDF Standard version {} requires support for ".format(version) +
            "{}, but no ExtensionType exists that explicitly ".format(tag) +
            "supports that version."
        )
