from __future__ import annotations

import pytest

from commitizen.version_schemes import SemVer, VersionProtocol
from tests.utils import VersionSchemeTestArgs


@pytest.mark.parametrize(
    "version_args, expected_version",
    [
        (
            VersionSchemeTestArgs(
                current_version="0.1.1",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.1.2",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.1.1",
                increment="MINOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.2.0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="2.1.1",
                increment="MAJOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "3.0.0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.9.0",
                increment="PATCH",
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.9.1-a0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.9.0",
                increment="MINOR",
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.10.0-a0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.9.0",
                increment="MAJOR",
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-a0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.9.0",
                increment="MAJOR",
                prerelease="alpha",
                prerelease_offset=1,
                devrelease=None,
            ),
            "1.0.0-a1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a2",
                increment=None,
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-b0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a2",
                increment=None,
                prerelease="beta",
                prerelease_offset=1,
                devrelease=None,
            ),
            "1.0.0-b1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0beta1",
                increment=None,
                prerelease="rc",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-rc0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0rc1",
                increment=None,
                prerelease="rc",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-rc2",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0-a0",
                increment=None,
                prerelease="rc",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-rc0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0-alpha1",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-a2",
        ),
        # weird cases
        (
            VersionSchemeTestArgs(
                current_version="1.1",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.1.1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1",
                increment="MINOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.1.0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1",
                increment="MAJOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "2.0.0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1a0",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-a1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1a0",
                increment=None,
                prerelease="alpha",
                prerelease_offset=1,
                devrelease=None,
            ),
            "1.0.0-a1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1",
                increment=None,
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-b0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1",
                increment=None,
                prerelease="beta",
                prerelease_offset=1,
                devrelease=None,
            ),
            "1.0.0-b1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1beta",
                increment=None,
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-b1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0alpha1",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-a2",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1",
                increment=None,
                prerelease="rc",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-rc0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0rc1+e20d7b57f3eb",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0",
        ),
        # simple flow
        (
            VersionSchemeTestArgs(
                current_version="0.1.0",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.1.1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.1.0",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=1,
            ),
            "0.1.1-dev1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.1.1",
                increment="MINOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.2.0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.2.0",
                increment="MINOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.3.0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.2.0",
                increment="MINOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=1,
            ),
            "0.3.0-dev1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.3.0",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.3.1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.3.0",
                increment="PATCH",
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.3.1-a0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.3.1a0",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.3.1-a1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.3.0",
                increment="PATCH",
                prerelease="alpha",
                prerelease_offset=1,
                devrelease=None,
            ),
            "0.3.1-a1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.3.1a0",
                increment=None,
                prerelease="alpha",
                prerelease_offset=1,
                devrelease=None,
            ),
            "0.3.1-a1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.3.1a0",
                increment=None,
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.3.1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.3.1",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.3.2",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.4.2",
                increment="MAJOR",
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-a0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a0",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-a1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a1",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-a2",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a1",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=1,
            ),
            "1.0.0-a2-dev1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a2.dev0",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=1,
            ),
            "1.0.0-a3-dev1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a2.dev0",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=0,
            ),
            "1.0.0-a3-dev0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a1",
                increment=None,
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-b0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0b0",
                increment=None,
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-b1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0b1",
                increment=None,
                prerelease="rc",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-rc0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0rc0",
                increment=None,
                prerelease="rc",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-rc1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0rc0",
                increment=None,
                prerelease="rc",
                prerelease_offset=0,
                devrelease=1,
            ),
            "1.0.0-rc1-dev1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0rc0",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a3.dev0",
                increment=None,
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.0-b0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.1",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.2",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.2",
                increment="MINOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.1.0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.1.0",
                increment="MINOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.2.0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.2.0",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.2.1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.2.1",
                increment="MAJOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "2.0.0",
        ),
        # linear prerelease cases (never bump backwards on pre-releases)
        (
            VersionSchemeTestArgs(
                current_version="0.1.1b1",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.1.1-b2",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.1.1rc0",
                increment=None,
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.1.1-rc1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="0.1.1rc0",
                increment=None,
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "0.1.1-rc1",
        ),
    ],
)
def test_bump_semver_version(
    version_args: VersionSchemeTestArgs, expected_version: str
):
    assert (
        str(
            SemVer(version_args.current_version).bump(
                increment=version_args.increment,
                prerelease=version_args.prerelease,
                prerelease_offset=version_args.prerelease_offset,
                devrelease=version_args.devrelease,
            )
        )
        == expected_version
    )


@pytest.mark.parametrize(
    "version_args, expected_version",
    [
        (
            VersionSchemeTestArgs(
                current_version="1.0.0",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="1.0.0",
                increment="MINOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.1.0",
        ),
        # with exact_increment=False: "1.0.0-b0"
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a1",
                increment="PATCH",
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.1-b0",
        ),
        # with exact_increment=False: "1.0.0-b1"
        (
            VersionSchemeTestArgs(
                current_version="1.0.0b0",
                increment="PATCH",
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.1-b0",
        ),
        # with exact_increment=False: "1.0.0-rc0"
        (
            VersionSchemeTestArgs(
                current_version="1.0.0b1",
                increment="PATCH",
                prerelease="rc",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.1-rc0",
        ),
        # with exact_increment=False: "1.0.0-rc1"
        (
            VersionSchemeTestArgs(
                current_version="1.0.0rc0",
                increment="PATCH",
                prerelease="rc",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.0.1-rc0",
        ),
        # with exact_increment=False: "1.0.0-rc1-dev1"
        (
            VersionSchemeTestArgs(
                current_version="1.0.0rc0",
                increment="PATCH",
                prerelease="rc",
                prerelease_offset=0,
                devrelease=1,
            ),
            "1.0.1-rc0-dev1",
        ),
        # with exact_increment=False: "1.0.0-b0"
        (
            VersionSchemeTestArgs(
                current_version="1.0.0a1",
                increment="MINOR",
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.1.0-b0",
        ),
        # with exact_increment=False: "1.0.0-b1"
        (
            VersionSchemeTestArgs(
                current_version="1.0.0b0",
                increment="MINOR",
                prerelease="beta",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.1.0-b0",
        ),
        # with exact_increment=False: "1.0.0-rc0"
        (
            VersionSchemeTestArgs(
                current_version="1.0.0b1",
                increment="MINOR",
                prerelease="rc",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.1.0-rc0",
        ),
        # with exact_increment=False: "1.0.0-rc1"
        (
            VersionSchemeTestArgs(
                current_version="1.0.0rc0",
                increment="MINOR",
                prerelease="rc",
                prerelease_offset=0,
                devrelease=None,
            ),
            "1.1.0-rc0",
        ),
        # with exact_increment=False: "1.0.0-rc1-dev1"
        (
            VersionSchemeTestArgs(
                current_version="1.0.0rc0",
                increment="MINOR",
                prerelease="rc",
                prerelease_offset=0,
                devrelease=1,
            ),
            "1.1.0-rc0-dev1",
        ),
        # with exact_increment=False: "2.0.0"
        (
            VersionSchemeTestArgs(
                current_version="2.0.0b0",
                increment="MAJOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "3.0.0",
        ),
        # with exact_increment=False: "2.0.0"
        (
            VersionSchemeTestArgs(
                current_version="2.0.0b0",
                increment="MINOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "2.1.0",
        ),
        # with exact_increment=False: "2.0.0"
        (
            VersionSchemeTestArgs(
                current_version="2.0.0b0",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "2.0.1",
        ),
        # same with exact_increment=False
        (
            VersionSchemeTestArgs(
                current_version="2.0.0b0",
                increment="MAJOR",
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "3.0.0-a0",
        ),
        # with exact_increment=False: "2.0.0b1"
        (
            VersionSchemeTestArgs(
                current_version="2.0.0b0",
                increment="MINOR",
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "2.1.0-a0",
        ),
        # with exact_increment=False: "2.0.0b1"
        (
            VersionSchemeTestArgs(
                current_version="2.0.0b0",
                increment="PATCH",
                prerelease="alpha",
                prerelease_offset=0,
                devrelease=None,
            ),
            "2.0.1-a0",
        ),
    ],
)
def test_bump_semver_version_force(
    version_args: VersionSchemeTestArgs, expected_version: str
):
    assert (
        str(
            SemVer(version_args.current_version).bump(
                increment=version_args.increment,
                prerelease=version_args.prerelease,
                prerelease_offset=version_args.prerelease_offset,
                devrelease=version_args.devrelease,
                exact_increment=True,
            )
        )
        == expected_version
    )


@pytest.mark.parametrize(
    "version_args, expected_version",
    [
        (
            VersionSchemeTestArgs(
                current_version="4.5.0+0.1.0",
                increment="PATCH",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "4.5.0+0.1.1",
        ),
        (
            VersionSchemeTestArgs(
                current_version="4.5.0+0.1.1",
                increment="MINOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "4.5.0+0.2.0",
        ),
        (
            VersionSchemeTestArgs(
                current_version="4.5.0+0.2.0",
                increment="MAJOR",
                prerelease=None,
                prerelease_offset=0,
                devrelease=None,
            ),
            "4.5.0+1.0.0",
        ),
    ],
)
def test_bump_semver_version_local(
    version_args: VersionSchemeTestArgs, expected_version: str
):
    assert (
        str(
            SemVer(version_args.current_version).bump(
                increment=version_args.increment,
                prerelease=version_args.prerelease,
                prerelease_offset=version_args.prerelease_offset,
                devrelease=version_args.devrelease,
                is_local_version=True,
            )
        )
        == expected_version
    )


def test_semver_scheme_property():
    version = SemVer("0.0.1")
    assert version.scheme is SemVer


def test_semver_implement_version_protocol():
    assert isinstance(SemVer("0.0.1"), VersionProtocol)
