"""test_mutator.py - mutator 모듈 테스트"""

import unittest
from unittest.mock import MagicMock, patch


class TestBuildMutationPrompt(unittest.TestCase):
    """build_mutation_prompt 함수 테스트"""

    def setUp(self):
        from autoresearch.mutator import MUTATION_PROMPT_TEMPLATE, build_mutation_prompt

        self.build_mutation_prompt = build_mutation_prompt
        self.template = MUTATION_PROMPT_TEMPLATE

    def test_variables_inserted(self):
        """템플릿 변수가 올바르게 삽입되는지 확인"""
        result = self.build_mutation_prompt(
            current_skill_md="# 스킬 내용",
            checklist_yaml="items:\n  - id: c1",
            recent_changelog="- 2024-01-01: 규칙 추가",
        )
        self.assertIn("# 스킬 내용", result)
        self.assertIn("items:\n  - id: c1", result)
        self.assertIn("- 2024-01-01: 규칙 추가", result)

    def test_returns_string(self):
        """문자열 반환 확인"""
        result = self.build_mutation_prompt("md", "yaml", "log")
        self.assertIsInstance(result, str)

    def test_prompt_contains_separator_instruction(self):
        """프롬프트에 구분자 지시 포함 확인"""
        result = self.build_mutation_prompt("md", "yaml", "log")
        self.assertIn("---MODIFIED_SKILL---", result)


class TestParseMutationResponse(unittest.TestCase):
    """parse_mutation_response 함수 테스트"""

    def setUp(self):
        from autoresearch.mutator import parse_mutation_response

        self.parse = parse_mutation_response

    def _make_response(self, mutation_type="규칙 추가", description="새 규칙 1개 추가", body="# 변경된 스킬"):
        return f"변경 유형: {mutation_type}\n" f"변경 설명: {description}\n" "\n" "---MODIFIED_SKILL---\n" f"{body}"

    def test_parse_normal_response(self):
        """정상 응답 파싱"""
        response = self._make_response(
            mutation_type="규칙 추가",
            description="금지 단어 목록 추가",
            body="# 스킬\n\n## 규칙\n- 새 규칙",
        )
        result = self.parse(response)
        self.assertEqual(result["mutation_type"], "규칙 추가")
        self.assertEqual(result["mutation_description"], "금지 단어 목록 추가")
        self.assertEqual(result["modified_skill_md"], "# 스킬\n\n## 규칙\n- 새 규칙")

    def test_parse_strips_whitespace(self):
        """값 앞뒤 공백 제거 확인"""
        response = "변경 유형:  예시 삽입  \n변경 설명:  좋은 예시 추가  \n\n---MODIFIED_SKILL---\n본문"
        result = self.parse(response)
        self.assertEqual(result["mutation_type"], "예시 삽입")
        self.assertEqual(result["mutation_description"], "좋은 예시 추가")

    def test_parse_multiline_body(self):
        """여러 줄 본문 파싱"""
        body = "# 제목\n\n줄1\n줄2\n줄3"
        response = self._make_response(body=body)
        result = self.parse(response)
        self.assertEqual(result["modified_skill_md"], body)

    def test_missing_separator_raises(self):
        """구분자 없으면 ValueError"""
        response = "변경 유형: 규칙 추가\n변경 설명: 설명\n\n본문만"
        with self.assertRaises(ValueError):
            self.parse(response)

    def test_missing_mutation_type_raises(self):
        """변경 유형 없으면 ValueError"""
        response = "변경 설명: 설명\n\n---MODIFIED_SKILL---\n본문"
        with self.assertRaises(ValueError):
            self.parse(response)

    def test_missing_mutation_description_raises(self):
        """변경 설명 없으면 ValueError"""
        response = "변경 유형: 규칙 추가\n\n---MODIFIED_SKILL---\n본문"
        with self.assertRaises(ValueError):
            self.parse(response)

    def test_empty_body_allowed(self):
        """빈 본문도 허용 (구분자 뒤에 아무것도 없는 경우)"""
        response = "변경 유형: 규칙 삭제\n변경 설명: 불필요 규칙 제거\n\n---MODIFIED_SKILL---\n"
        result = self.parse(response)
        self.assertEqual(result["modified_skill_md"], "")

    def test_result_keys(self):
        """반환 dict 키 확인"""
        result = self.parse(self._make_response())
        self.assertIn("mutation_type", result)
        self.assertIn("mutation_description", result)
        self.assertIn("modified_skill_md", result)


class TestGenerateMutation(unittest.TestCase):
    """generate_mutation 함수 테스트 (call_claude 모킹)"""

    @patch("autoresearch.mutator.call_claude")
    def test_generate_returns_parsed_result_with_tokens(self, mock_call_claude):
        """generate_mutation이 파싱 결과 + 토큰 정보 반환"""
        llm_text = "변경 유형: 규칙 추가\n" "변경 설명: 테스트 규칙\n" "\n" "---MODIFIED_SKILL---\n" "# 변경된 스킬"
        mock_call_claude.return_value = llm_text

        from autoresearch.mutator import generate_mutation

        result = generate_mutation(
            current_skill_md="# 원본",
            checklist_yaml="items: []",
            recent_changelog="없음",
            model="claude-sonnet-4-6",
        )

        self.assertEqual(result["mutation_type"], "규칙 추가")
        self.assertEqual(result["mutation_description"], "테스트 규칙")
        self.assertEqual(result["modified_skill_md"], "# 변경된 스킬")
        self.assertGreater(result["input_tokens"], 0)
        self.assertGreater(result["output_tokens"], 0)

    @patch("autoresearch.mutator.call_claude")
    def test_generate_uses_correct_model(self, mock_call_claude):
        """지정한 모델로 call_claude 호출 확인"""
        llm_text = "변경 유형: 예시 삽입\n" "변경 설명: 예시 추가\n" "\n" "---MODIFIED_SKILL---\n" "본문"
        mock_call_claude.return_value = llm_text

        from autoresearch.mutator import generate_mutation

        generate_mutation("md", "yaml", "log", model="claude-opus-4-6")

        call_kwargs = mock_call_claude.call_args
        self.assertEqual(call_kwargs.kwargs["model"], "claude-opus-4-6")

    @patch("autoresearch.mutator.call_claude")
    def test_generate_max_tokens_8192(self, mock_call_claude):
        """max_tokens=8192로 call_claude 호출 확인"""
        llm_text = "변경 유형: 표현 수정\n" "변경 설명: 명확화\n" "\n" "---MODIFIED_SKILL---\n" "본문"
        mock_call_claude.return_value = llm_text

        from autoresearch.mutator import generate_mutation

        generate_mutation("md", "yaml", "log")

        call_kwargs = mock_call_claude.call_args
        self.assertEqual(call_kwargs.kwargs["max_tokens"], 8192)

    @patch("autoresearch.mutator.call_claude")
    def test_generate_calls_call_claude(self, mock_call_claude):
        """call_claude가 호출되는지 확인"""
        llm_text = "변경 유형: 구조 변경\n" "변경 설명: 구조 변경\n" "\n" "---MODIFIED_SKILL---\n" "본문"
        mock_call_claude.return_value = llm_text

        from autoresearch.mutator import generate_mutation

        generate_mutation("md", "yaml", "log")

        mock_call_claude.assert_called_once()


if __name__ == "__main__":
    unittest.main()
