"""
통합 테스트: scripts/codex_gate_check.py
task-1837_5.1 - 닌기르수 작성
"""

import json
import sys
from unittest.mock import MagicMock, patch

sys.path.insert(0, "/home/jay/workspace/scripts")
import codex_gate_check


# ── 1. _extract_json_from_output ────────────────────────────────────────────

def test_extract_json_from_output_code_block():
    """```json ... ``` 코드 블록에서 JSON을 추출해야 한다."""
    output = 'Here is the result:\n```json\n{"pass": true, "risks": []}\n```\n'
    result = codex_gate_check._extract_json_from_output(output)
    assert result is not None
    assert result["pass"] is True
    assert result["risks"] == []


def test_extract_json_from_output_direct_json():
    """코드 블록 없이 직접 JSON 객체가 있을 때 추출해야 한다."""
    output = 'Some text {"pass": false, "risks": [{"severity": "critical", "description": "test"}]} end'
    result = codex_gate_check._extract_json_from_output(output)
    assert result is not None
    assert result["pass"] is False
    assert len(result["risks"]) == 1


def test_extract_json_from_output_invalid_returns_none():
    """JSON이 없거나 잘못된 형식이면 None을 반환해야 한다."""
    result = codex_gate_check._extract_json_from_output("No JSON here at all.")
    assert result is None


def test_extract_json_from_output_broken_json_in_block():
    """코드 블록 내 JSON이 파싱 불가이면 직접 JSON도 시도하고 없으면 None 반환."""
    output = "```json\n{broken json\n```\nalso no direct json"
    result = codex_gate_check._extract_json_from_output(output)
    assert result is None


def test_extract_json_from_output_code_block_takes_priority():
    """코드 블록이 있으면 직접 JSON보다 코드 블록을 우선 처리해야 한다."""
    output = '```json\n{"source": "block"}\n```\n{"source": "direct"}'
    result = codex_gate_check._extract_json_from_output(output)
    assert result is not None
    assert result["source"] == "block"


# ── 2. _maat_fallback_check ──────────────────────────────────────────────────

def test_maat_fallback_check_task_file_missing(tmp_path):
    """task_file이 없으면 critical 리스크가 포함되고 pass=False여야 한다."""
    missing_task = str(tmp_path / "nonexistent_task.md")
    result = codex_gate_check._maat_fallback_check(missing_task, [], str(tmp_path))
    assert result["pass"] is False
    assert result["source"] == "maat_fallback"
    assert any(r["severity"] == "critical" for r in result["risks"])


def test_maat_fallback_check_task_file_exists_pass(tmp_path):
    """task_file이 존재하고 affected_files도 있으면 pass=True여야 한다."""
    task_file = tmp_path / "task.md"
    task_file.write_text("# 설계 문서\n내용이 있습니다.", encoding="utf-8")
    affected = tmp_path / "code.py"
    affected.write_text("def foo(): pass", encoding="utf-8")

    result = codex_gate_check._maat_fallback_check(
        str(task_file), [str(affected)], str(tmp_path)
    )
    assert result["pass"] is True
    assert result["risks"] == []
    assert result["source"] == "maat_fallback"


def test_maat_fallback_check_affected_file_missing(tmp_path):
    """task_file은 존재하지만 affected_files 중 하나가 없으면 critical 리스크 발생."""
    task_file = tmp_path / "task.md"
    task_file.write_text("# 설계 문서\n내용", encoding="utf-8")
    missing_file = str(tmp_path / "not_here.py")

    result = codex_gate_check._maat_fallback_check(
        str(task_file), [missing_file], str(tmp_path)
    )
    assert result["pass"] is False
    critical_descs = [r["description"] for r in result["risks"] if r["severity"] == "critical"]
    assert any("not_here.py" in d for d in critical_descs)


def test_maat_fallback_check_empty_task_file(tmp_path):
    """task_file이 존재하지만 비어있으면 critical 리스크가 발생해야 한다."""
    task_file = tmp_path / "empty_task.md"
    task_file.write_text("   ", encoding="utf-8")  # 공백만

    result = codex_gate_check._maat_fallback_check(str(task_file), [], str(tmp_path))
    assert result["pass"] is False
    assert any(r["severity"] == "critical" for r in result["risks"])


# ── 3. codex_gate_check (subprocess.run mock) ────────────────────────────────

def test_codex_gate_check_success(tmp_path):
    """Codex CLI가 pass=True JSON을 반환하면 결과도 pass=True여야 한다."""
    task_file = tmp_path / "task.md"
    task_file.write_text("# 설계 문서\n내용", encoding="utf-8")

    mock_result = MagicMock()
    mock_result.returncode = 0
    mock_result.stdout = json.dumps({
        "pass": True,
        "risks": [],
        "suggestions": ["Good design"],
    })
    mock_result.stderr = ""

    with patch("subprocess.run", return_value=mock_result):
        result = codex_gate_check.codex_gate_check(
            str(task_file), [], str(tmp_path)
        )

    assert result["pass"] is True
    assert result["risks"] == []
    assert result["source"] == "codex_companion"
    assert result["error"] is None


def test_codex_gate_check_with_critical_risk(tmp_path):
    """Codex CLI가 critical 리스크를 반환하면 pass=False여야 한다."""
    task_file = tmp_path / "task.md"
    task_file.write_text("# 설계 문서", encoding="utf-8")

    mock_result = MagicMock()
    mock_result.returncode = 0
    mock_result.stdout = json.dumps({
        "risks": [{"severity": "critical", "description": "SQL injection risk"}],
        "suggestions": ["Fix it"],
    })
    mock_result.stderr = ""

    with patch("subprocess.run", return_value=mock_result):
        result = codex_gate_check.codex_gate_check(
            str(task_file), [], str(tmp_path)
        )

    assert result["pass"] is False
    assert result["source"] == "codex_companion"
    assert any(r["severity"] == "critical" for r in result["risks"])


def test_codex_gate_check_nonzero_returncode_falls_back_to_maat(tmp_path):
    """Codex CLI 비정상 종료(returncode!=0) 시 maat_fallback으로 전환해야 한다."""
    task_file = tmp_path / "task.md"
    task_file.write_text("# 설계 문서\n내용", encoding="utf-8")

    mock_result = MagicMock()
    mock_result.returncode = 1
    mock_result.stdout = ""
    mock_result.stderr = "Error: codex failed"

    with patch("subprocess.run", return_value=mock_result):
        result = codex_gate_check.codex_gate_check(
            str(task_file), [], str(tmp_path)
        )

    assert result["source"] == "maat_fallback"


def test_codex_gate_check_timeout_falls_back_to_maat(tmp_path):
    """Codex CLI 타임아웃 시 maat_fallback으로 전환해야 한다."""
    import subprocess

    task_file = tmp_path / "task.md"
    task_file.write_text("# 설계 문서\n내용", encoding="utf-8")

    with patch("subprocess.run", side_effect=subprocess.TimeoutExpired(cmd="codex", timeout=120)):
        result = codex_gate_check.codex_gate_check(
            str(task_file), [], str(tmp_path)
        )

    assert result["source"] == "maat_fallback"


def test_codex_gate_check_file_not_found_falls_back_to_maat(tmp_path):
    """Codex CLI 실행파일 없을 때 FileNotFoundError → maat_fallback으로 전환해야 한다."""
    task_file = tmp_path / "task.md"
    task_file.write_text("# 설계 문서\n내용", encoding="utf-8")

    with patch("subprocess.run", side_effect=FileNotFoundError("codex not found")):
        result = codex_gate_check.codex_gate_check(
            str(task_file), [], str(tmp_path)
        )

    assert result["source"] == "maat_fallback"


def test_codex_gate_check_invalid_json_response_falls_back_to_maat(tmp_path):
    """Codex CLI가 JSON 파싱 불가 응답 반환 시 maat_fallback으로 전환해야 한다."""
    task_file = tmp_path / "task.md"
    task_file.write_text("# 설계 문서\n내용", encoding="utf-8")

    mock_result = MagicMock()
    mock_result.returncode = 0
    mock_result.stdout = "This is not JSON at all!"
    mock_result.stderr = ""

    with patch("subprocess.run", return_value=mock_result):
        result = codex_gate_check.codex_gate_check(
            str(task_file), [], str(tmp_path)
        )

    assert result["source"] == "maat_fallback"
