"""test_claude_runner.py - claude_runner 모듈 테스트"""

import os
import subprocess
import unittest
from unittest.mock import MagicMock, patch


class TestCallClaude(unittest.TestCase):
    """call_claude() 함수 테스트"""

    @patch("autoresearch.claude_runner.subprocess.run")
    def test_basic_call_success(self, mock_run):
        """기본 호출 성공"""
        mock_run.return_value = MagicMock(returncode=0, stdout="Hello World\n", stderr="")
        from autoresearch.claude_runner import call_claude

        result = call_claude("Say hello")
        self.assertEqual(result, "Hello World")
        mock_run.assert_called_once()

    @patch("autoresearch.claude_runner.subprocess.run")
    def test_cmd_includes_required_args(self, mock_run):
        """cmd에 필수 인자 포함 확인"""
        mock_run.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
        from autoresearch.claude_runner import call_claude

        call_claude("test", model="claude-haiku-4-5-20251001")
        cmd = mock_run.call_args[0][0]
        self.assertIn("claude", cmd)
        self.assertIn("-p", cmd)
        self.assertIn("test", cmd)
        self.assertIn("--output-format", cmd)
        self.assertIn("text", cmd)
        self.assertIn("--model", cmd)
        self.assertIn("claude-haiku-4-5-20251001", cmd)
        self.assertIn("--max-turns", cmd)
        self.assertIn("1", cmd)

    @patch("autoresearch.claude_runner.subprocess.run")
    def test_system_prompt_passed(self, mock_run):
        """system 파라미터 전달 시 --system-prompt 포함"""
        mock_run.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
        from autoresearch.claude_runner import call_claude

        call_claude("input", system="You are a helper")
        cmd = mock_run.call_args[0][0]
        self.assertIn("--system-prompt", cmd)
        self.assertIn("You are a helper", cmd)

    @patch("autoresearch.claude_runner.subprocess.run")
    def test_no_system_prompt_when_none(self, mock_run):
        """system=None이면 --system-prompt 미포함"""
        mock_run.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
        from autoresearch.claude_runner import call_claude

        call_claude("input")
        cmd = mock_run.call_args[0][0]
        self.assertNotIn("--system-prompt", cmd)

    @patch("autoresearch.claude_runner.subprocess.run")
    def test_claudecode_env_removed(self, mock_run):
        """CLAUDECODE 환경변수가 제거되는지 확인"""
        mock_run.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
        from autoresearch.claude_runner import call_claude

        with patch.dict(os.environ, {"CLAUDECODE": "1"}):
            call_claude("test")
        env = mock_run.call_args[1]["env"]
        self.assertNotIn("CLAUDECODE", env)

    @patch("autoresearch.claude_runner.subprocess.run")
    def test_nonzero_exit_raises_runtime_error(self, mock_run):
        """비정상 exit code 시 RuntimeError"""
        mock_run.return_value = MagicMock(returncode=1, stdout="", stderr="error msg")
        from autoresearch.claude_runner import call_claude

        with self.assertRaises(RuntimeError) as ctx:
            call_claude("test")
        self.assertIn("error msg", str(ctx.exception))

    @patch("autoresearch.claude_runner.subprocess.run")
    def test_timeout_passed(self, mock_run):
        """timeout 파라미터 전달 확인"""
        mock_run.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
        from autoresearch.claude_runner import call_claude

        call_claude("test", timeout=60)
        self.assertEqual(mock_run.call_args[1]["timeout"], 60)

    @patch("autoresearch.claude_runner.subprocess.run")
    def test_timeout_expired_propagated(self, mock_run):
        """TimeoutExpired 예외 전파"""
        mock_run.side_effect = subprocess.TimeoutExpired(cmd="claude", timeout=120)
        from autoresearch.claude_runner import call_claude

        with self.assertRaises(subprocess.TimeoutExpired):
            call_claude("test")

    @patch("autoresearch.claude_runner.subprocess.run")
    def test_output_stripped(self, mock_run):
        """출력 앞뒤 공백 제거 확인"""
        mock_run.return_value = MagicMock(returncode=0, stdout="  hello  \n\n", stderr="")
        from autoresearch.claude_runner import call_claude

        result = call_claude("test")
        self.assertEqual(result, "hello")


class TestEstimateTokens(unittest.TestCase):
    """estimate_tokens() 함수 테스트"""

    def test_basic_estimation(self):
        from autoresearch.claude_runner import estimate_tokens

        # 100 chars -> 25 tokens
        self.assertEqual(estimate_tokens("a" * 100), 25)

    def test_empty_string_returns_one(self):
        from autoresearch.claude_runner import estimate_tokens

        # 빈 문자열 -> 최소 1
        self.assertEqual(estimate_tokens(""), 1)

    def test_short_string_returns_one(self):
        from autoresearch.claude_runner import estimate_tokens

        # 3 chars -> 0 -> max(1, 0) = 1
        self.assertEqual(estimate_tokens("abc"), 1)

    def test_korean_text(self):
        from autoresearch.claude_runner import estimate_tokens

        # 한글 8자 -> 8 * 3 bytes but len() is char count
        text = "안녕하세요테스트"  # 8 chars
        self.assertEqual(estimate_tokens(text), 2)


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