import pytest

from poetry.core.semver.empty_constraint import EmptyConstraint
from poetry.core.semver.version import Version
from poetry.core.semver.version_range import VersionRange


@pytest.fixture()
def v003() -> Version:
    return Version.parse("0.0.3")


@pytest.fixture()
def v010() -> Version:
    return Version.parse("0.1.0")


@pytest.fixture()
def v080() -> Version:
    return Version.parse("0.8.0")


@pytest.fixture()
def v072() -> Version:
    return Version.parse("0.7.2")


@pytest.fixture()
def v114() -> Version:
    return Version.parse("1.1.4")


@pytest.fixture()
def v123() -> Version:
    return Version.parse("1.2.3")


@pytest.fixture()
def v124() -> Version:
    return Version.parse("1.2.4")


@pytest.fixture()
def v130() -> Version:
    return Version.parse("1.3.0")


@pytest.fixture()
def v140() -> Version:
    return Version.parse("1.4.0")


@pytest.fixture()
def v200() -> Version:
    return Version.parse("2.0.0")


@pytest.fixture()
def v234() -> Version:
    return Version.parse("2.3.4")


@pytest.fixture()
def v250() -> Version:
    return Version.parse("2.5.0")


@pytest.fixture()
def v300() -> Version:
    return Version.parse("3.0.0")


@pytest.fixture()
def v300b1() -> Version:
    return Version.parse("3.0.0b1")


@pytest.mark.parametrize(
    "base,other",
    [
        pytest.param(Version.parse("3.0.0"), Version.parse("3.0.0-1"), id="post"),
        pytest.param(
            Version.parse("3.0.0"), Version.parse("3.0.0+local.1"), id="local"
        ),
    ],
)
def test_allows_post_releases_with_max(base: Version, other: Version):
    range = VersionRange(max=base, include_max=True)
    assert range.allows(other)


@pytest.mark.parametrize(
    "base,other",
    [
        pytest.param(Version.parse("3.0.0"), Version.parse("3.0.0-1"), id="post"),
        pytest.param(
            Version.parse("3.0.0"), Version.parse("3.0.0+local.1"), id="local"
        ),
    ],
)
def test_allows_post_releases_with_min(base: Version, other: Version):
    range = VersionRange(min=base, include_min=True)
    assert range.allows(other)


def test_allows_post_releases_with_post_and_local_min():
    one = Version.parse("3.0.0+local.1")
    two = Version.parse("3.0.0-1")
    three = Version.parse("3.0.0-1+local.1")
    four = Version.parse("3.0.0+local.2")

    assert VersionRange(min=one, include_min=True).allows(two)
    assert VersionRange(min=one, include_min=True).allows(three)
    assert VersionRange(min=one, include_min=True).allows(four)

    assert not VersionRange(min=two, include_min=True).allows(one)
    assert VersionRange(min=two, include_min=True).allows(three)
    assert not VersionRange(min=two, include_min=True).allows(four)

    assert not VersionRange(min=three, include_min=True).allows(one)
    assert not VersionRange(min=three, include_min=True).allows(two)
    assert not VersionRange(min=three, include_min=True).allows(four)

    assert not VersionRange(min=four, include_min=True).allows(one)
    assert VersionRange(min=four, include_min=True).allows(two)
    assert VersionRange(min=four, include_min=True).allows(three)


def test_allows_post_releases_with_post_and_local_max():
    one = Version.parse("3.0.0+local.1")
    two = Version.parse("3.0.0-1")
    three = Version.parse("3.0.0-1+local.1")
    four = Version.parse("3.0.0+local.2")

    assert VersionRange(max=one, include_max=True).allows(two)
    assert VersionRange(max=one, include_max=True).allows(three)
    assert not VersionRange(max=one, include_max=True).allows(four)

    assert VersionRange(max=two, include_max=True).allows(one)
    assert VersionRange(max=two, include_max=True).allows(three)
    assert VersionRange(max=two, include_max=True).allows(four)

    assert VersionRange(max=three, include_max=True).allows(one)
    assert VersionRange(max=three, include_max=True).allows(two)
    assert VersionRange(max=three, include_max=True).allows(four)

    assert VersionRange(max=four, include_max=True).allows(one)
    assert VersionRange(max=four, include_max=True).allows(two)
    assert VersionRange(max=four, include_max=True).allows(three)


@pytest.mark.parametrize(
    "base,one,two",
    [
        pytest.param(
            Version.parse("3.0.0"),
            Version.parse("3.0.0-1"),
            Version.parse("3.0.0-2"),
            id="post",
        ),
        pytest.param(
            Version.parse("3.0.0"),
            Version.parse("3.0.0+local.1"),
            Version.parse("3.0.0+local.2"),
            id="local",
        ),
    ],
)
def test_allows_post_releases_explicit_with_max(
    base: Version, one: Version, two: Version
):
    range = VersionRange(max=one, include_max=True)
    assert range.allows(base)
    assert not range.allows(two)

    range = VersionRange(max=two, include_max=True)
    assert range.allows(base)
    assert range.allows(one)


@pytest.mark.parametrize(
    "base,one,two",
    [
        pytest.param(
            Version.parse("3.0.0"),
            Version.parse("3.0.0-1"),
            Version.parse("3.0.0-2"),
            id="post",
        ),
        pytest.param(
            Version.parse("3.0.0"),
            Version.parse("3.0.0+local.1"),
            Version.parse("3.0.0+local.2"),
            id="local",
        ),
    ],
)
def test_allows_post_releases_explicit_with_min(
    base: Version, one: Version, two: Version
):
    range = VersionRange(min=one, include_min=True)
    assert not range.allows(base)
    assert range.allows(two)

    range = VersionRange(min=two, include_min=True)
    assert not range.allows(base)
    assert not range.allows(one)


def test_allows_all(
    v123: Version, v124: Version, v140: Version, v250: Version, v300: Version
):
    assert VersionRange(v123, v250).allows_all(EmptyConstraint())

    range = VersionRange(v123, v250, include_max=True)
    assert not range.allows_all(v123)
    assert range.allows_all(v124)
    assert range.allows_all(v250)
    assert not range.allows_all(v300)


def test_allows_all_with_no_min(
    v080: Version, v140: Version, v250: Version, v300: Version
):
    range = VersionRange(max=v250)
    assert range.allows_all(VersionRange(v080, v140))
    assert not range.allows_all(VersionRange(v080, v300))
    assert range.allows_all(VersionRange(max=v140))
    assert not range.allows_all(VersionRange(max=v300))
    assert range.allows_all(range)
    assert not range.allows_all(VersionRange())


def test_allows_all_with_no_max(
    v003: Version, v010: Version, v080: Version, v140: Version
):
    range = VersionRange(min=v010)
    assert range.allows_all(VersionRange(v080, v140))
    assert not range.allows_all(VersionRange(v003, v140))
    assert range.allows_all(VersionRange(v080))
    assert not range.allows_all(VersionRange(v003))
    assert range.allows_all(range)
    assert not range.allows_all(VersionRange())


def test_allows_all_bordering_range_not_more_inclusive(v010: Version, v250: Version):
    # Allows bordering range that is not more inclusive
    exclusive = VersionRange(v010, v250)
    inclusive = VersionRange(v010, v250, True, True)