# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Test the primer commands. """
from __future__ import annotations

import sys
from pathlib import Path
from unittest.mock import patch

import pytest
from _pytest.capture import CaptureFixture

from pylint.constants import IS_PYPY
from pylint.testutils._primer.primer import Primer

HERE = Path(__file__).parent
TEST_DIR_ROOT = HERE.parent.parent
PRIMER_DIRECTORY = TEST_DIR_ROOT / ".pylint_primer_tests/"
PACKAGES_TO_PRIME_PATH = TEST_DIR_ROOT / "primer/packages_to_prime.json"
FIXTURES_PATH = HERE / "fixtures"

PRIMER_CURRENT_INTERPRETER = (3, 11)

DEFAULT_ARGS = ["python tests/primer/__main__.py", "compare", "--commit=v2.14.2"]


@pytest.mark.parametrize("args", [[], ["wrong_command"]])
def test_primer_launch_bad_args(args: list[str], capsys: CaptureFixture) -> None:
    with pytest.raises(SystemExit):
        with patch("sys.argv", ["python tests/primer/__main__.py", *args]):
            Primer(PRIMER_DIRECTORY, PACKAGES_TO_PRIME_PATH).run()
    out, err = capsys.readouterr()
    assert not out
    assert "usage: Pylint Primer" in err


@pytest.mark.skipif(
    sys.version_info[:2] != PRIMER_CURRENT_INTERPRETER or IS_PYPY,
    reason=(
        "Primers are internal and will always be run for only one interpreter (currently"
        f" {PRIMER_CURRENT_INTERPRETER})"
    ),
)
class TestPrimer:
    @pytest.mark.parametrize(
        "directory",
        [
            pytest.param(p, id=str(p.relative_to(FIXTURES_PATH)))
            for p in FIXTURES_PATH.iterdir()
            if p.is_dir() and p.name != "batched"  # tested separately
        ],
    )
    def test_compare(self, directory: Path) -> None:
        """Test for the standard case.

        Directory in 'fixtures/' with 'main.json', 'pr.json' and 'expected.txt'."""
        self.__assert_expected(directory)

    def test_compare_batched(self) -> None:
        fixture = FIXTURES_PATH / "batched"
        self.__assert_expected(
            fixture,
            fixture / "main_BATCHIDX.json",
            fixture / "pr_BATCHIDX.json",
            batches=2,
        )

    def test_truncated_compare(self) -> None:
        """Test for the truncation of comments that are too long."""
        max_comment_length = 525
        directory = FIXTURES_PATH / "message_changed"
        with patch(
            "pylint.testutils._primer.primer_compare_command.MAX_GITHUB_COMMENT_LENGTH",
            max_comment_length,
        ):
            content = self.__assert_expected(
                directory, expected_file=directory / "expected_truncated.txt"
            )
        assert len(content) < max_comment_length

    @staticmethod
    def __assert_expected(
        directory: Path,
        main: Path | None = None,
        pr: Path | None = None,
        expected_file: Path | None = None,
        batches: int = 0,
    ) -> str:
        if main is None:
            main = directory / "main.json"
        if pr is None:
            pr = directory / "pr.json"
        if expected_file is None:
            expected_file = directory / "expected.txt"
        new_argv = [*DEFAULT_ARGS, f"--base-file={main}", f"--new-file={pr}"]
        if batches:
            new_argv.append(f"--batches={batches}")
        with patch("sys.argv", new_argv):
            Primer(PRIMER_DIRECTORY, PACKAGES_TO_PRIME_PATH).run()
        with open(PRIMER_DIRECTORY / "comment.txt", encoding="utf8") as f:
            content = f.read()
        with open(expected_file, encoding="utf8") as f:
            expected = f.read()
        # rstrip so the expected.txt can end with a newline
        assert content == expected.rstrip("\n")
        return content
