"""Tests for PR creation and description helpers."""

from __future__ import annotations

import os
from unittest.mock import MagicMock, patch

import pytest

from codeflash_python.codegen._create_pr import (
    _get_pr_number,
    _github_pr_url,
    check_create_pr,
)


class TestGetPrNumber:
    """Tests for _get_pr_number."""

    def test_returns_int_when_set(self, monkeypatch) -> None:
        """Returns integer PR number from environment."""
        monkeypatch.setenv("CODEFLASH_PR_NUMBER", "42")

        assert 42 == _get_pr_number()

    def test_returns_none_when_unset(self, monkeypatch) -> None:
        """Returns None when env var is missing."""
        monkeypatch.delenv("CODEFLASH_PR_NUMBER", raising=False)

        assert _get_pr_number() is None

    def test_returns_none_when_empty(self, monkeypatch) -> None:
        """Returns None when env var is empty string."""
        monkeypatch.setenv("CODEFLASH_PR_NUMBER", "")

        assert _get_pr_number() is None


class TestGithubPrUrl:
    """Tests for _github_pr_url."""

    def test_builds_url(self) -> None:
        """URL is built from owner, repo, and PR id."""
        assert "https://github.com/org/repo/pull/123" == _github_pr_url(
            "org", "repo", "123"
        )


class TestCheckCreatePr:
    """Tests for check_create_pr."""

    def _base_kwargs(self) -> dict:
        return {
            "platform_client": MagicMock(),
            "git_repo": MagicMock(),
            "original_code": {"file.py": "old"},
            "new_code": {"file.py": "new"},
            "pr_comment": MagicMock(),
            "existing_tests": "test source",
            "generated_tests": "",
            "function_trace_id": "trace-1",
            "coverage_message": "",
        }

    def test_no_changes_returns_early(self) -> None:
        """When old and new code are identical, no PR is created."""
        kwargs = self._base_kwargs()
        kwargs["original_code"] = {"file.py": "same"}
        kwargs["new_code"] = {"file.py": "same"}

        check_create_pr(**kwargs)

        kwargs["platform_client"].suggest_changes.assert_not_called()
        kwargs["platform_client"].create_pr.assert_not_called()

    @patch(
        "codeflash_python.codegen._create_pr.get_repo_owner_and_name",
        return_value=("org", "repo"),
    )
    def test_suggests_changes_when_pr_number_set(
        self,
        _mock_owner: MagicMock,
        monkeypatch,
    ) -> None:
        """With CODEFLASH_PR_NUMBER, suggests changes to existing PR."""
        monkeypatch.setenv("CODEFLASH_PR_NUMBER", "99")
        kwargs = self._base_kwargs()
        kwargs["platform_client"].suggest_changes.return_value = MagicMock(
            ok=True,
        )

        check_create_pr(**kwargs)

        kwargs["platform_client"].suggest_changes.assert_called_once()
        call_kwargs = kwargs[
            "platform_client"
        ].suggest_changes.call_args.kwargs
        assert 99 == call_kwargs["pr_number"]

    @patch(
        "codeflash_python.codegen._create_pr.get_repo_owner_and_name",
        return_value=("org", "repo"),
    )
    def test_suggest_changes_failure_logs_error(
        self,
        _mock_owner: MagicMock,
        monkeypatch,
        caplog,
    ) -> None:
        """Failed suggestion logs an error."""
        monkeypatch.setenv("CODEFLASH_PR_NUMBER", "99")
        kwargs = self._base_kwargs()
        kwargs["platform_client"].suggest_changes.return_value = MagicMock(
            ok=False,
            text="forbidden",
        )

        with caplog.at_level("ERROR"):
            check_create_pr(**kwargs)

        assert "Failed to suggest changes" in caplog.text

    @patch(
        "codeflash_python.codegen._create_pr.check_and_push_branch",
        return_value=True,
    )
    @patch(
        "codeflash_python.codegen._create_pr.get_repo_owner_and_name",
        return_value=("org", "repo"),
    )
    def test_creates_new_pr_when_no_pr_number(
        self,
        _mock_owner: MagicMock,
        _mock_push: MagicMock,
        monkeypatch,
    ) -> None:
        """Without PR number, creates a new PR via platform."""
        monkeypatch.delenv("CODEFLASH_PR_NUMBER", raising=False)
        kwargs = self._base_kwargs()
        kwargs["git_repo"].active_branch.name = "feature-branch"
        kwargs["platform_client"].create_pr.return_value = MagicMock(
            ok=True,
            text="456",
        )

        check_create_pr(**kwargs)

        kwargs["platform_client"].create_pr.assert_called_once()
        call_kwargs = kwargs["platform_client"].create_pr.call_args.kwargs
        assert "feature-branch" == call_kwargs["base_branch"]

    @patch(
        "codeflash_python.codegen._create_pr.check_and_push_branch",
        return_value=False,
    )
    @patch(
        "codeflash_python.codegen._create_pr.get_repo_owner_and_name",
        return_value=("org", "repo"),
    )
    def test_skips_pr_when_branch_not_pushed(
        self,
        _mock_owner: MagicMock,
        _mock_push: MagicMock,
        monkeypatch,
    ) -> None:
        """When branch push fails, PR creation is skipped."""
        monkeypatch.delenv("CODEFLASH_PR_NUMBER", raising=False)
        kwargs = self._base_kwargs()

        check_create_pr(**kwargs)

        kwargs["platform_client"].create_pr.assert_not_called()

    @patch(
        "codeflash_python.codegen._create_pr.check_and_push_branch",
        return_value=True,
    )
    @patch(
        "codeflash_python.codegen._create_pr.get_repo_owner_and_name",
        return_value=("org", "repo"),
    )
    def test_github_app_not_installed_warning(
        self,
        _mock_owner: MagicMock,
        _mock_push: MagicMock,
        monkeypatch,
        caplog,
    ) -> None:
        """401 with 'not installed' logs a user-friendly warning."""
        monkeypatch.delenv("CODEFLASH_PR_NUMBER", raising=False)
        kwargs = self._base_kwargs()
        kwargs["git_repo"].active_branch.name = "main"
        kwargs["platform_client"].create_pr.return_value = MagicMock(
            ok=False,
            status_code=401,
            text="GitHub App not installed on this repo",
        )

        with caplog.at_level("WARNING"):
            check_create_pr(**kwargs)

        assert "not installed" in caplog.text

    @patch(
        "codeflash_python.codegen._create_pr.check_and_push_branch",
        return_value=True,
    )
    @patch(
        "codeflash_python.codegen._create_pr.get_repo_owner_and_name",
        return_value=("org", "repo"),
    )
    def test_create_pr_failure_logs_error(
        self,
        _mock_owner: MagicMock,
        _mock_push: MagicMock,
        monkeypatch,
        caplog,
    ) -> None:
        """Non-401 failure logs a generic error."""
        monkeypatch.delenv("CODEFLASH_PR_NUMBER", raising=False)
        kwargs = self._base_kwargs()
        kwargs["git_repo"].active_branch.name = "main"
        kwargs["platform_client"].create_pr.return_value = MagicMock(
            ok=False,
            status_code=500,
            text="internal error",
        )

        with caplog.at_level("ERROR"):
            check_create_pr(**kwargs)

        assert "Failed to create PR" in caplog.text
