"""test_qc_verify.py - qc_verify.py 자동 추론 + MANUAL_SKIP 테스트"""

import importlib
import json
import os
import sys
import tempfile

import pytest

# qc_verify.py 경로 추가
QC_DIR = "/home/jay/workspace/teams/dev1/qc"
sys.path.insert(0, QC_DIR)

from qc_verify import (
    _determine_overall,
    _infer_test_files,
    _summarize,
    run_check,
)
from verifiers import test_runner

# ---------------------------------------------------------------------------
# 1. _infer_test_files - 존재하는 테스트 파일 발견
# ---------------------------------------------------------------------------


class TestInferTestFilesFound:
    """_infer_test_files에 실제 존재하는 파일 경로를 넣으면 관련 테스트 파일 반환"""

    def test_infer_test_files_found(self, tmp_path, monkeypatch):
        """임시 디렉토리에 tests/test_foo.py 생성 → foo.py 경로로 추론 → 발견 확인"""
        # 구조: tmp_path/src/foo.py, tmp_path/src/tests/test_foo.py
        src_dir = tmp_path / "src"
        src_dir.mkdir()
        tests_dir = src_dir / "tests"
        tests_dir.mkdir()

        foo_py = src_dir / "foo.py"
        foo_py.write_text("# foo")

        test_foo_py = tests_dir / "test_foo.py"
        test_foo_py.write_text("# test foo")

        monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))

        result = _infer_test_files([str(foo_py)])
        assert str(test_foo_py) in result, f"test_foo.py가 추론 결과에 있어야 함, got: {result}"

    def test_infer_test_files_found_parent_tests(self, tmp_path, monkeypatch):
        """부모 디렉토리의 tests/test_foo.py도 발견"""
        # 구조: tmp_path/src/sub/foo.py, tmp_path/src/tests/test_foo.py
        sub_dir = tmp_path / "src" / "sub"
        sub_dir.mkdir(parents=True)
        tests_dir = tmp_path / "src" / "tests"
        tests_dir.mkdir()

        foo_py = sub_dir / "foo.py"
        foo_py.write_text("# foo")

        test_foo_py = tests_dir / "test_foo.py"
        test_foo_py.write_text("# test foo")

        monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))

        result = _infer_test_files([str(foo_py)])
        assert str(test_foo_py) in result, f"부모 tests/test_foo.py가 추론 결과에 있어야 함, got: {result}"

    def test_infer_test_files_found_workspace_tests(self, tmp_path, monkeypatch):
        """workspace_root/tests/test_foo.py 패턴 발견"""
        monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))

        workspace_tests_dir = tmp_path / "tests"
        workspace_tests_dir.mkdir()

        src_dir = tmp_path / "src"
        src_dir.mkdir()

        foo_py = src_dir / "foo.py"
        foo_py.write_text("# foo")

        test_foo_py = workspace_tests_dir / "test_foo.py"
        test_foo_py.write_text("# test foo")

        result = _infer_test_files([str(foo_py)])
        assert str(test_foo_py) in result, f"workspace tests/test_foo.py가 추론 결과에 있어야 함, got: {result}"


# ---------------------------------------------------------------------------
# 2. _infer_test_files - 관련 테스트 파일 없음 → 빈 리스트
# ---------------------------------------------------------------------------


class TestInferTestFilesNotFound:
    """관련 테스트 파일이 없으면 빈 리스트 반환"""

    def test_infer_test_files_not_found(self, tmp_path, monkeypatch):
        """존재하지 않는 파일 경로 전달 → 빈 리스트"""
        monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))

        fake_path = str(tmp_path / "src" / "nonexistent.py")
        result = _infer_test_files([fake_path])
        assert result == [], f"테스트 파일 없으면 빈 리스트, got: {result}"

    def test_infer_empty_list(self, tmp_path, monkeypatch):
        """빈 리스트 전달 → 빈 리스트"""
        monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))
        result = _infer_test_files([])
        assert result == []


# ---------------------------------------------------------------------------
# 3. _infer_test_files - test_ 접두사 파일은 건너뜀
# ---------------------------------------------------------------------------


class TestInferTestFilesSkipTestFiles:
    """test_ 접두사 파일은 건너뜀"""

    def test_infer_test_files_skip_test_files(self, tmp_path, monkeypatch):
        """test_foo.py 전달 → 빈 리스트"""
        monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))

        test_file = tmp_path / "test_foo.py"
        test_file.write_text("# test")

        result = _infer_test_files([str(test_file)])
        assert result == [], f"test_ 접두사 파일은 건너뜀, got: {result}"


# ---------------------------------------------------------------------------
# 4. _infer_test_files - .py가 아닌 파일은 건너뜀
# ---------------------------------------------------------------------------


class TestInferTestFilesSkipNonPython:
    """.py가 아닌 파일은 건너뜀"""

    def test_infer_test_files_skip_non_python(self, tmp_path, monkeypatch):
        """js, txt, md 파일 전달 → 빈 리스트"""
        monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))

        js_file = tmp_path / "foo.js"
        js_file.write_text("// js")
        txt_file = tmp_path / "foo.txt"
        txt_file.write_text("txt")
        md_file = tmp_path / "README.md"
        md_file.write_text("# md")

        result = _infer_test_files([str(js_file), str(txt_file), str(md_file)])
        assert result == [], f".py 아닌 파일은 건너뜀, got: {result}"


# ---------------------------------------------------------------------------
# 5. run_check - test_runner MANUAL_SKIP
# ---------------------------------------------------------------------------


class TestRunCheckTestRunnerManualSkip:
    """--skip test_runner 시 MANUAL_SKIP 상태 반환"""

    def test_run_check_test_runner_manual_skip(self):
        """run_check('test_runner', skip_list=['test_runner'], ...) → status == 'MANUAL_SKIP'"""
        result = run_check(
            name="test_runner",
            skip_list=["test_runner"],
            task_id="task-test",
            api_base="",
            api_endpoints=[],
            test_dir="",
            file_paths=[],
        )
        assert result["status"] == "MANUAL_SKIP", f"MANUAL_SKIP 기대, got: {result}"

    def test_manual_skip_details_contains_warning(self):
        """MANUAL_SKIP details에 경고 메시지 포함"""
        result = run_check(
            name="test_runner",
            skip_list=["test_runner"],
            task_id="task-test",
            api_base="",
            api_endpoints=[],
            test_dir="",
            file_paths=[],
        )
        assert result["status"] == "MANUAL_SKIP"
        details_str = "\n".join(result["details"])
        assert "MANUAL_SKIP" in details_str or "경고" in details_str or "WARN" in details_str.upper()


# ---------------------------------------------------------------------------
# 6. run_check - test_runner 자동 추론, 테스트 파일 0개 → SKIP
# ---------------------------------------------------------------------------


class TestRunCheckTestRunnerAutoInferNoFiles:
    """check_files로 추론했으나 테스트 파일 0개 → SKIP"""

    def test_run_check_test_runner_auto_infer_no_files(self, tmp_path, monkeypatch):
        """check_files로 추론했으나 테스트 파일 0개 → SKIP"""
        monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))

        # 존재하는 py 파일이지만 관련 테스트 파일 없음
        src_file = tmp_path / "bar.py"
        src_file.write_text("# bar")

        result = run_check(
            name="test_runner",
            skip_list=[],
            task_id="task-test",
            api_base="",
            api_endpoints=[],
            test_dir="",
            file_paths=[str(src_file)],
        )
        assert result["status"] == "SKIP", f"테스트 파일 0개 → SKIP 기대, got: {result}"

    def test_run_check_test_runner_no_dir_no_files_skip(self):
        """test_dir도 없고 file_paths도 없으면 SKIP"""
        result = run_check(
            name="test_runner",
            skip_list=[],
            task_id="task-test",
            api_base="",
            api_endpoints=[],
            test_dir="",
            file_paths=[],
        )
        assert result["status"] == "SKIP", f"아무것도 없으면 SKIP, got: {result}"


# ---------------------------------------------------------------------------
# 7. run_check - test_dir 사용 시 stderr에 WARN 출력
# ---------------------------------------------------------------------------


class TestRunCheckTestRunnerWithTestDirWarns:
    """test_dir 사용 시 stderr에 WARN 출력"""

    def test_run_check_test_runner_with_test_dir_warns(self, tmp_path, capsys):
        """test_dir 사용 시 stderr에 --test-dir 관련 경고 출력"""
        # 실제 테스트 디렉토리 생성 (존재하지 않으면 FAIL 반환)
        test_dir = tmp_path / "tests"
        test_dir.mkdir()

        result = run_check(
            name="test_runner",
            skip_list=[],
            task_id="task-test",
            api_base="",
            api_endpoints=[],
            test_dir=str(test_dir),
            file_paths=[],
        )
        captured = capsys.readouterr()
        assert (
            "--test-dir" in captured.err or "test-dir" in captured.err
        ), f"stderr에 --test-dir 관련 경고 기대, got: {captured.err!r}"


# ---------------------------------------------------------------------------
# 8. _summarize - MANUAL_SKIP 카운트 포함
# ---------------------------------------------------------------------------


class TestSummarizeWithManualSkip:
    """MANUAL_SKIP이 _summarize에 반영"""

    def test_summarize_with_manual_skip(self):
        """MANUAL_SKIP이 summary에 포함"""
        checks = {
            "test_runner": {"status": "MANUAL_SKIP", "details": []},
            "file_check": {"status": "PASS", "details": []},
            "data_integrity": {"status": "PASS", "details": []},
        }
        summary = _summarize(checks)
        assert "MANUAL_SKIP" in summary, f"MANUAL_SKIP이 summary에 있어야 함, got: {summary!r}"
        assert "2 PASS" in summary or "PASS" in summary

    def test_summarize_no_manual_skip_no_entry(self):
        """MANUAL_SKIP 없으면 summary에 미포함"""
        checks = {
            "file_check": {"status": "PASS", "details": []},
            "data_integrity": {"status": "SKIP", "details": []},
        }
        summary = _summarize(checks)
        assert "MANUAL_SKIP" not in summary


# ---------------------------------------------------------------------------
# 9. _determine_overall - MANUAL_SKIP 포함 시 WARN
# ---------------------------------------------------------------------------


class TestDetermineOverallManualSkipIsWarn:
    """MANUAL_SKIP 포함 시 전체 결과가 WARN"""

    def test_determine_overall_manual_skip_is_warn(self):
        """MANUAL_SKIP이 있으면 overall WARN"""
        checks = {
            "test_runner": {"status": "MANUAL_SKIP", "details": []},
            "file_check": {"status": "PASS", "details": []},
        }
        overall = _determine_overall(checks, [])
        assert overall == "WARN", f"MANUAL_SKIP → WARN 기대, got: {overall}"

    def test_determine_overall_fail_beats_manual_skip(self):
        """FAIL + MANUAL_SKIP 혼재 시 FAIL"""
        checks = {
            "test_runner": {"status": "MANUAL_SKIP", "details": []},
            "file_check": {"status": "FAIL", "details": []},
        }
        overall = _determine_overall(checks, [])
        assert overall == "FAIL", f"FAIL이 있으면 FAIL, got: {overall}"

    def test_determine_overall_all_pass_no_manual_skip(self):
        """모두 PASS면 PASS"""
        checks = {
            "file_check": {"status": "PASS", "details": []},
            "data_integrity": {"status": "PASS", "details": []},
        }
        overall = _determine_overall(checks, [])
        assert overall == "PASS"


# ---------------------------------------------------------------------------
# 10. test_runner.verify(test_files=[...]) 동작 확인
# ---------------------------------------------------------------------------


class TestVerifyWithTestFiles:
    """test_runner.verify(test_files=[...]) 동작 확인"""

    @pytest.fixture(autouse=True)
    def _ensure_real_test_runner(self):
        """test_qc_gate.py의 sys.modules MagicMock 오염 방지를 위해 실제 모듈로 교체"""
        import unittest.mock

        # sys.modules에서 MagicMock 오염 제거 후 진짜 모듈 로드
        for mod_name in list(sys.modules.keys()):
            if "verifiers" in mod_name and isinstance(sys.modules[mod_name], unittest.mock.MagicMock):
                del sys.modules[mod_name]

    def test_verify_with_test_files_empty_list_skip(self):
        """빈 test_files → SKIP"""
        import verifiers.test_runner as tr

        result = tr.verify(test_files=[])
        assert result["status"] == "SKIP", f"빈 test_files → SKIP 기대, got: {result}"

    def test_verify_with_nonexistent_test_files_skip(self):
        """존재하지 않는 test_files → SKIP (존재하는 파일 0개)"""
        import verifiers.test_runner as tr

        result = tr.verify(test_files=["/tmp/nonexistent_test_file_abc123.py"])
        assert result["status"] == "SKIP", f"존재하지 않는 파일 → SKIP 기대, got: {result}"
        details_str = "\n".join(result["details"])
        assert "존재하는" in details_str or "없음" in details_str or "SKIP" in details_str.upper()

    def test_verify_with_existing_test_file(self, tmp_path):
        """존재하는 테스트 파일 → pytest 실행 (PASS 또는 FAIL)"""
        import verifiers.test_runner as tr

        # 간단한 테스트 파일 생성
        test_file = tmp_path / "test_sample.py"
        test_file.write_text("def test_always_pass():\n    assert 1 == 1\n")

        result = tr.verify(test_files=[str(test_file)])
        # pytest 실행 결과이므로 PASS, FAIL, SKIP 중 하나
        assert result["status"] in ("PASS", "FAIL", "SKIP"), f"유효한 상태 기대, got: {result}"

    def test_verify_with_no_args_skip(self):
        """test_dir도 test_files도 없으면 SKIP"""
        import verifiers.test_runner as tr

        result = tr.verify()
        assert result["status"] == "SKIP", f"인수 없으면 SKIP 기대, got: {result}"

    def test_verify_test_files_overrides_test_dir(self, tmp_path):
        """test_files가 주어지면 test_dir 무시하지 않고 test_files로만 실행
        (test_files가 None이 아니면 test_dir 경로보다 test_files 우선)"""
        import verifiers.test_runner as tr

        nonexistent_dir = str(tmp_path / "nonexistent_dir")

        # test_files에 존재하지 않는 파일만 있으면 SKIP
        result = tr.verify(
            test_dir=nonexistent_dir,
            test_files=["/tmp/nonexistent_test_xyz.py"],
        )
        # test_files 기반으로만 동작해서 존재하는 파일 0개 → SKIP
        assert result["status"] == "SKIP", f"test_files 우선 → SKIP 기대, got: {result}"
