"""gfo.commands.review のテスト。"""

from __future__ import annotations

import json

import pytest

from gfo.adapter.base import Review
from gfo.commands import review as review_cmd
from gfo.exceptions import ConfigError, HttpError
from tests.test_commands.conftest import make_args, patch_adapter

SAMPLE_REVIEW = Review(
    id=1,
    state="approved",
    body="Looks good",
    author="reviewer",
    url="",
    submitted_at="2024-01-01T00:00:00Z",
)


class TestHandleList:
    def test_calls_list_reviews(self, capsys):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.list_reviews.return_value = [SAMPLE_REVIEW]
            args = make_args(number=1)
            review_cmd.handle_list(args, fmt="table")
        adapter.list_reviews.assert_called_once_with(1)

    def test_outputs_results(self, capsys):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.list_reviews.return_value = [SAMPLE_REVIEW]
            args = make_args(number=1)
            review_cmd.handle_list(args, fmt="table")
        out = capsys.readouterr().out
        assert "approved" in out

    def test_json_output(self, capsys):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.list_reviews.return_value = [SAMPLE_REVIEW]
            args = make_args(number=1)
            review_cmd.handle_list(args, fmt="json")
        out = capsys.readouterr().out
        data = json.loads(out)
        assert isinstance(data, list)
        assert data[0]["state"] == "approved"

    def test_error_propagation(self):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.list_reviews.side_effect = HttpError(500, "Server error")
            args = make_args(number=1)
            with pytest.raises(HttpError):
                review_cmd.handle_list(args, fmt="table")


class TestHandleCreate:
    def test_approve(self):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.create_review.return_value = SAMPLE_REVIEW
            args = make_args(number=1, approve=True, request_changes=False, comment=False, body="")
            review_cmd.handle_create(args, fmt="table")
        adapter.create_review.assert_called_once_with(1, state="APPROVE", body="")

    def test_request_changes(self):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.create_review.return_value = SAMPLE_REVIEW
            args = make_args(
                number=1, approve=False, request_changes=True, comment=False, body="needs work"
            )
            review_cmd.handle_create(args, fmt="table")
        adapter.create_review.assert_called_once_with(1, state="REQUEST_CHANGES", body="needs work")

    def test_comment_without_body_raises(self):
        with patch_adapter("gfo.commands.review"):
            args = make_args(number=1, approve=False, request_changes=False, comment=True, body="")
            with pytest.raises(ConfigError):
                review_cmd.handle_create(args, fmt="table")

    def test_json_output(self, capsys):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.create_review.return_value = SAMPLE_REVIEW
            args = make_args(number=1, approve=True, request_changes=False, comment=False, body="")
            review_cmd.handle_create(args, fmt="json")
        out = capsys.readouterr().out
        data = json.loads(out)
        assert isinstance(data, list)
        assert data[0]["state"] == "approved"

    def test_error_propagation(self):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.create_review.side_effect = HttpError(403, "Forbidden")
            args = make_args(number=1, approve=True, request_changes=False, comment=False, body="")
            with pytest.raises(HttpError):
                review_cmd.handle_create(args, fmt="table")


class TestHandleReview:
    """handle_review ディスパッチャのテスト。"""

    def test_dispatches_to_list(self, capsys):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.list_reviews.return_value = [SAMPLE_REVIEW]
            args = make_args(review_action="list", number=1)
            review_cmd.handle_review(args, fmt="table")
        adapter.list_reviews.assert_called_once_with(1)

    def test_dispatches_to_create(self):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.create_review.return_value = SAMPLE_REVIEW
            args = make_args(
                review_action="create",
                number=1,
                approve=True,
                request_changes=False,
                comment=False,
                body="",
            )
            review_cmd.handle_review(args, fmt="table")
        adapter.create_review.assert_called_once_with(1, state="APPROVE", body="")

    def test_dispatches_to_dismiss(self):
        with patch_adapter("gfo.commands.review") as adapter:
            args = make_args(review_action="dismiss", number=1, review_id=42, message="outdated")
            review_cmd.handle_review(args, fmt="table")
        adapter.dismiss_review.assert_called_once_with(1, 42, message="outdated")

    def test_no_action_raises(self):
        args = make_args(review_action=None)
        with pytest.raises(ConfigError):
            review_cmd.handle_review(args, fmt="table")


class TestHandleDismiss:
    def test_calls_dismiss_review(self):
        with patch_adapter("gfo.commands.review") as adapter:
            args = make_args(number=1, review_id=42, message="outdated")
            review_cmd.handle_dismiss(args, fmt="table")
        adapter.dismiss_review.assert_called_once_with(1, 42, message="outdated")

    def test_dismiss_without_message(self):
        with patch_adapter("gfo.commands.review") as adapter:
            args = make_args(number=1, review_id=10, message=None)
            review_cmd.handle_dismiss(args, fmt="table")
        adapter.dismiss_review.assert_called_once_with(1, 10, message="")

    def test_error_propagation(self):
        with patch_adapter("gfo.commands.review") as adapter:
            adapter.dismiss_review.side_effect = HttpError(404, "Not found")
            args = make_args(number=1, review_id=42, message="outdated")
            with pytest.raises(HttpError):
                review_cmd.handle_dismiss(args, fmt="table")
