from __future__ import annotations

from pathlib import Path

import pytest

from commitizen.changelog import Metadata
from commitizen.changelog_formats.markdown import Markdown
from commitizen.config.base_config import BaseConfig

CHANGELOG_A = """
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Start using "changelog" over "change log" since it's the common usage.

## [1.0.0] - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
""".strip()

EXPECTED_A = Metadata(
    latest_version="1.0.0",
    latest_version_position=10,
    unreleased_end=10,
    unreleased_start=7,
)


CHANGELOG_B = """
## [Unreleased]
- Start using "changelog" over "change log" since it's the common usage.

## 1.2.0
""".strip()

EXPECTED_B = Metadata(
    latest_version="1.2.0",
    latest_version_position=3,
    unreleased_end=3,
    unreleased_start=0,
)


CHANGELOG_C = """
# Unreleased

## v1.0.0
"""
EXPECTED_C = Metadata(
    latest_version="1.0.0",
    latest_version_tag="v1.0.0",
    latest_version_position=3,
    unreleased_end=3,
    unreleased_start=1,
)

CHANGELOG_D = """
## Unreleased
- Start using "changelog" over "change log" since it's the common usage.
"""

EXPECTED_D = Metadata(
    latest_version=None,
    latest_version_position=None,
    unreleased_end=2,
    unreleased_start=1,
)

CHANGELOG_E = """
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Start using "changelog" over "change log" since it's the common usage.

## {tag_formatted_version} - 2017-06-20
### Added
- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
- Version navigation.
""".strip()

EXPECTED_E = Metadata(
    latest_version="1.0.0",
    latest_version_position=10,
    unreleased_end=10,
    unreleased_start=7,
)


@pytest.fixture
def format(config: BaseConfig) -> Markdown:
    return Markdown(config)


@pytest.fixture
def format_with_tags(config: BaseConfig, request) -> Markdown:
    config.settings["tag_format"] = request.param
    config.settings["legacy_tag_formats"] = ["legacy-${version}"]
    return Markdown(config)


VERSIONS_EXAMPLES = [
    ("## [1.0.0] - 2017-06-20", ("1.0.0", "1.0.0")),
    (
        "# [10.0.0-next.3](https://github.com/angular/angular/compare/10.0.0-next.2...10.0.0-next.3) (2020-04-22)",
        ("10.0.0-next.3", "10.0.0-next.3"),
    ),
    ("### 0.19.1 (Jan 7, 2020)", ("0.19.1", "0.19.1")),
    ("## 1.0.0", ("1.0.0", "1.0.0")),
    ("## v1.0.0", ("1.0.0", "v1.0.0")),
    ("## v1.0.0 - (2012-24-32)", ("1.0.0", "v1.0.0")),
    ("# version 2020.03.24", ("2020.03.24", "2020.03.24")),
    ("## [Unreleased]", None),
    ("All notable changes to this project will be documented in this file.", None),
    ("# Changelog", None),
    ("### Bug Fixes", None),
]


@pytest.mark.parametrize("line_from_changelog,output_version", VERSIONS_EXAMPLES)
def test_changelog_detect_version(
    line_from_changelog: str, output_version: tuple[str, str] | None, format: Markdown
):
    version = format.parse_version_from_title(line_from_changelog)
    assert version == output_version


TITLES_EXAMPLES = [
    ("## [1.0.0] - 2017-06-20", 2),
    ("## [Unreleased]", 2),
    ("# Unreleased", 1),
]


@pytest.mark.parametrize("line_from_changelog,output_title", TITLES_EXAMPLES)
def test_parse_title_type_of_line(
    line_from_changelog: str, output_title: str, format: Markdown
):
    title = format.parse_title_level(line_from_changelog)
    assert title == output_title


@pytest.mark.parametrize(
    "content, expected",
    (
        pytest.param(CHANGELOG_A, EXPECTED_A, id="A"),
        pytest.param(CHANGELOG_B, EXPECTED_B, id="B"),
        pytest.param(CHANGELOG_C, EXPECTED_C, id="C"),
        pytest.param(CHANGELOG_D, EXPECTED_D, id="D"),
    ),
)
def test_get_metadata(
    tmp_path: Path, format: Markdown, content: str, expected: Metadata
):
    changelog = tmp_path / format.default_changelog_file
    changelog.write_text(content)

    assert format.get_metadata(str(changelog)) == expected


@pytest.mark.parametrize(
    "format_with_tags, tag_string, expected, ",
    (
        pytest.param("${version}-example", "1.0.0-example", "1.0.0"),
        pytest.param("${version}example", "1.0.0example", "1.0.0"),
        pytest.param("example${version}", "example1.0.0", "1.0.0"),
        pytest.param("example-${version}", "example-1.0.0", "1.0.0"),
        pytest.param("example-${major}-${minor}-${patch}", "example-1-0-0", "1.0.0"),
        pytest.param("example-${major}-${minor}", "example-1-0-0", None),
        pytest.param(
            "${major}-${minor}-${patch}-${prerelease}-example",
            "1-0-0-rc1-example",
            "1.0.0-rc1",
        ),
        pytest.param(
            "${major}-${minor}-${patch}-${prerelease}-example",
            "1-0-0-a1-example",
            "1.0.0-a1",
        ),
        pytest.param(
            "${major}-${minor}-${patch}-${prerelease}${devrelease}-example",
            "1-0-0-a1.dev1-example",
            "1.0.0-a1.dev1",
        ),
        pytest.param("new-${version}", "legacy-1.0.0", "1.0.0"),
    ),
    indirect=["format_with_tags"],
)
def test_get_metadata_custom_tag_format(
    tmp_path: Path, format_with_tags: Markdown, tag_string: str, expected: Metadata
):
    content = CHANGELOG_E.format(tag_formatted_version=tag_string)
    changelog = tmp_path / format_with_tags.default_changelog_file
    changelog.write_text(content)

    assert format_with_tags.get_metadata(str(changelog)).latest_version == expected
