# pyright: reportMissingImports=false
"""
Tests for impact_analyzer.py
"""

import json
import os
import subprocess
import sys
import time
from pathlib import Path

import pytest  # noqa: F401

_PARENT = str(Path(__file__).resolve().parent.parent)
if _PARENT not in sys.path:
    sys.path.insert(0, _PARENT)

from impact_analyzer import (  # noqa: E402
    analyze,
    build_dependency_chain,
    extract_endpoints,
    extract_functions,
    extract_imports,
)

FIXTURES_DIR = Path(__file__).parent / "fixtures"
ANALYZER_PATH = Path(__file__).parent.parent / "impact_analyzer.py"


# ---------------------------------------------------------------------------
# 시나리오 테스트 (5개)
# ---------------------------------------------------------------------------


class TestScenarios:
    """5개 핵심 시나리오 검증"""

    def test_server_file_extracts_endpoints(self):
        """시나리오 1: sample_server.py 분석 시 /api/wiki/refine/start 포함"""
        server_file = str(FIXTURES_DIR / "sample_server.py")
        result = analyze([server_file], workspace=str(FIXTURES_DIR))

        assert (
            "/api/wiki/refine/start" in result["affected_endpoints"]
        ), f"Expected /api/wiki/refine/start in affected_endpoints, got: {result['affected_endpoints']}"

    def test_module_file_extracts_functions(self):
        """시나리오 2: sample_module.py 분석 시 extract_data 포함"""
        module_file = str(FIXTURES_DIR / "sample_module.py")
        result = analyze([module_file], workspace=str(FIXTURES_DIR))

        assert (
            "extract_data" in result["affected_functions"]
        ), f"Expected extract_data in affected_functions, got: {result['affected_functions']}"

    def test_empty_files_graceful(self):
        """시나리오 3: 빈 파일 목록 → 빈 결과 반환 (에러 없음)"""
        result = analyze([], workspace=str(FIXTURES_DIR))

        assert result["changed_files"] == []
        assert result["affected_functions"] == []
        assert result["affected_endpoints"] == []
        assert result["affected_imports"] == []
        assert result["dependency_chain"] == {}

    def test_nonexistent_file_ignored(self):
        """시나리오 4: 존재하지 않는 파일 → 에러 없이 무시"""
        result = analyze(
            ["/nonexistent/path/fake_file.py"],
            workspace=str(FIXTURES_DIR),
        )

        assert isinstance(result, dict)
        assert "affected_functions" in result
        assert "affected_endpoints" in result

    def test_execution_within_30_seconds(self):
        """시나리오 5: 30초 이내 완료"""
        files = [
            str(FIXTURES_DIR / "sample_server.py"),
            str(FIXTURES_DIR / "sample_module.py"),
            str(FIXTURES_DIR / "sample_utils.py"),
        ]

        start = time.time()
        analyze(files, workspace=str(FIXTURES_DIR))
        elapsed = time.time() - start

        assert elapsed < 30, f"Execution took {elapsed:.2f}s, expected < 30s"


# ---------------------------------------------------------------------------
# 단위 테스트
# ---------------------------------------------------------------------------


class TestExtractFunctionsFromAST:

    def test_extracts_top_level_functions(self):
        result = extract_functions(str(FIXTURES_DIR / "sample_utils.py"))
        assert "helper_func" in result
        assert "format_output" in result

    def test_extracts_class_methods(self):
        result = extract_functions(str(FIXTURES_DIR / "sample_module.py"))
        assert "extract_data" in result
        assert "process_data" in result
        assert "DataProcessor" in result
        assert "DataProcessor.run" in result

    def test_nonexistent_file_returns_empty(self):
        result = extract_functions("/nonexistent/file.py")
        assert result == []


class TestExtractImportsFromAST:

    def test_extracts_from_imports(self):
        result = extract_imports(str(FIXTURES_DIR / "sample_module.py"))
        assert "sample_utils" in result

    def test_extracts_plain_imports(self):
        result = extract_imports(str(FIXTURES_DIR / "sample_importer.py"))
        assert "sample_module" in result
        assert "sample_utils" in result

    def test_nonexistent_file_returns_empty(self):
        result = extract_imports("/nonexistent/file.py")
        assert result == []


class TestExtractEndpoints:

    def test_extracts_path_comparison_endpoints(self):
        result = extract_endpoints(str(FIXTURES_DIR / "sample_server.py"))
        assert "/api/wiki/refine/start" in result
        assert "/api/wiki/refine/resume" in result
        assert "/api/status" in result

    def test_extracts_all_three_endpoints(self):
        result = extract_endpoints(str(FIXTURES_DIR / "sample_server.py"))
        assert len(result) >= 3

    def test_nonexistent_file_returns_empty(self):
        result = extract_endpoints("/nonexistent/file.py")
        assert result == []

    def test_file_without_endpoints_returns_empty(self):
        result = extract_endpoints(str(FIXTURES_DIR / "sample_utils.py"))
        assert result == []


class TestBuildDependencyChain:

    def test_finds_files_importing_module(self):
        module_file = str(FIXTURES_DIR / "sample_module.py")
        chain = build_dependency_chain([module_file], workspace=str(FIXTURES_DIR))

        assert module_file in chain
        importers = chain[module_file]
        importer_names = [os.path.basename(p) for p in importers]
        assert any(
            name in importer_names for name in ["sample_server.py", "sample_importer.py"]
        ), f"Expected importers in chain, got: {importer_names}"

    def test_empty_files_returns_empty_chain(self):
        chain = build_dependency_chain([], workspace=str(FIXTURES_DIR))
        assert chain == {}

    def test_nonexistent_file_returns_empty_list(self):
        chain = build_dependency_chain(["/nonexistent/file.py"], workspace=str(FIXTURES_DIR))
        assert isinstance(chain, dict)


class TestCLIOutputJSON:
    """CLI 실행 시 JSON 파일 출력"""

    def test_cli_produces_json_output(self, tmp_path: Path):
        output_file = tmp_path / "impact.json"
        server_file = str(FIXTURES_DIR / "sample_server.py")

        result = subprocess.run(
            [
                sys.executable,
                str(ANALYZER_PATH),
                "--files",
                server_file,
                "--output",
                str(output_file),
                "--workspace",
                str(FIXTURES_DIR),
            ],
            capture_output=True,
            text=True,
            timeout=30,
        )

        assert result.returncode == 0, f"CLI failed: {result.stderr}"
        assert output_file.exists(), "Output JSON file was not created"

        with open(output_file) as f:
            data = json.load(f)

        assert "changed_files" in data
        assert "affected_functions" in data
        assert "affected_endpoints" in data
        assert "affected_imports" in data
        assert "dependency_chain" in data

    def test_cli_json_contains_endpoints(self, tmp_path: Path):
        output_file = tmp_path / "impact.json"
        server_file = str(FIXTURES_DIR / "sample_server.py")

        subprocess.run(
            [
                sys.executable,
                str(ANALYZER_PATH),
                "--files",
                server_file,
                "--output",
                str(output_file),
                "--workspace",
                str(FIXTURES_DIR),
            ],
            capture_output=True,
            text=True,
            timeout=30,
        )

        with open(output_file) as f:
            data = json.load(f)

        assert "/api/wiki/refine/start" in data["affected_endpoints"]
