"""
End-to-End Scenario Tests
=========================

ユーザーシナリオに基づくE2Eテスト。

実際のMCPサーバーを起動し、クライアント経由でテストします。
"""

import subprocess
import sys
from pathlib import Path

import pytest


# ========================
# Helper Functions
# ========================

def create_sample_project(repo_path: Path) -> None:
    """サンプルプロジェクトを作成"""
    # Python files
    src_dir = repo_path / "src"
    src_dir.mkdir(parents=True, exist_ok=True)

    # Main module
    (src_dir / "main.py").write_text('''"""Main application module."""

from calculator import Calculator
from utils import format_result


def main():
    """Main entry point."""
    calc = Calculator()
    result = calc.add(1, 2)
    print(format_result(result))


if __name__ == "__main__":
    main()
''')

    # Calculator module
    (src_dir / "calculator.py").write_text('''"""Calculator module with basic operations."""

from typing import Union

Number = Union[int, float]


class Calculator:
    """A simple calculator class."""

    def __init__(self):
        self.history = []

    def add(self, a: Number, b: Number) -> Number:
        """Add two numbers."""
        result = a + b
        self._record(f"{a} + {b} = {result}")
        return result

    def subtract(self, a: Number, b: Number) -> Number:
        """Subtract b from a."""
        result = a - b
        self._record(f"{a} - {b} = {result}")
        return result

    def multiply(self, a: Number, b: Number) -> Number:
        """Multiply two numbers."""
        result = a * b
        self._record(f"{a} * {b} = {result}")
        return result

    def divide(self, a: Number, b: Number) -> Number:
        """Divide a by b."""
        if b == 0:
            raise ValueError("Cannot divide by zero")
        result = a / b
        self._record(f"{a} / {b} = {result}")
        return result

    def _record(self, operation: str) -> None:
        """Record operation in history."""
        self.history.append(operation)

    def get_history(self) -> list[str]:
        """Get operation history."""
        return self.history.copy()
''')

    # Utils module
    (src_dir / "utils.py").write_text('''"""Utility functions."""


def format_result(value: float, precision: int = 2) -> str:
    """Format a numeric result."""
    return f"Result: {value:.{precision}f}"


def validate_number(value: str) -> float:
    """Validate and convert string to number."""
    try:
        return float(value)
    except ValueError:
        raise ValueError(f"Invalid number: {value}")
''')

    # Test file
    tests_dir = repo_path / "tests"
    tests_dir.mkdir(exist_ok=True)

    (tests_dir / "test_calculator.py").write_text('''"""Tests for calculator module."""

import pytest
from src.calculator import Calculator


class TestCalculator:
    """Test cases for Calculator class."""

    def setup_method(self):
        """Set up test fixtures."""
        self.calc = Calculator()

    def test_add(self):
        """Test addition."""
        assert self.calc.add(1, 2) == 3

    def test_subtract(self):
        """Test subtraction."""
        assert self.calc.subtract(5, 3) == 2

    def test_multiply(self):
        """Test multiplication."""
        assert self.calc.multiply(3, 4) == 12

    def test_divide(self):
        """Test division."""
        assert self.calc.divide(10, 2) == 5

    def test_divide_by_zero(self):
        """Test division by zero raises error."""
        with pytest.raises(ValueError):
            self.calc.divide(10, 0)

    def test_history(self):
        """Test operation history."""
        self.calc.add(1, 1)
        self.calc.multiply(2, 3)
        history = self.calc.get_history()
        assert len(history) == 2
''')

    # Git commit
    subprocess.run(["git", "add", "."], check=False, cwd=repo_path, capture_output=True)
    subprocess.run(
        ["git", "commit", "-m", "Initial commit"],
        check=False, cwd=repo_path,
        capture_output=True,
    )


def create_typescript_files(repo_path: Path) -> None:
    """TypeScriptファイルを追加"""
    ts_dir = repo_path / "frontend"
    ts_dir.mkdir(exist_ok=True)

    (ts_dir / "api.ts").write_text('''/**
 * API client module
 */

interface CalculatorResult {
    operation: string;
    result: number;
}

class APIClient {
    private baseUrl: string;

    constructor(baseUrl: string) {
        this.baseUrl = baseUrl;
    }

    async calculate(operation: string, a: number, b: number): Promise<CalculatorResult> {
        const response = await fetch(`${this.baseUrl}/calculate`, {
            method: 'POST',
            body: JSON.stringify({ operation, a, b }),
        });
        return response.json();
    }
}

export { APIClient, CalculatorResult };
''')

    subprocess.run(["git", "add", "."], check=False, cwd=repo_path, capture_output=True)
    subprocess.run(
        ["git", "commit", "-m", "Add TypeScript files"],
        check=False, cwd=repo_path,
        capture_output=True,
    )


# ========================
# Test Classes
# ========================

class TestCodebaseAnalysisScenario:
    """コードベース分析シナリオ"""

    @pytest.mark.asyncio
    @pytest.mark.e2e
    async def test_index_and_query_codebase(self, temp_repo):
        """
        シナリオ: リポジトリをインデックスしてクエリする

        1. サンプルリポジトリを作成
        2. indexerでインデックス
        3. GraphEngineでクエリ
        """
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.graph import GraphEngine, GraphQuery
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.indexer import Indexer

        # 1. サンプルプロジェクトを作成
        create_sample_project(temp_repo)

        # 2. インデックス作成（incremental=Falseで全ファイルをスキャン）
        indexer = Indexer()
        result = await indexer.index_repository(temp_repo, incremental=False)

        # ファイルが見つかっていることを確認
        assert result.files_indexed > 0 or result.entities_count >= 0

        # 3. クエリ実行
        engine = GraphEngine(temp_repo)
        await engine.initialize()

        try:
            query = GraphQuery(query="Calculator", max_results=10)
            query_result = await engine.query(query)

            # クエリが正常に実行されることを確認
            assert query_result is not None
        finally:
            await engine.close()

    @pytest.mark.asyncio
    @pytest.mark.e2e
    async def test_find_code_relationships(self, temp_repo):
        """
        シナリオ: コードの関連性を探索する

        1. サンプルコードをインデックス
        2. エンティティ間の関係を検索
        """
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.graph import GraphEngine, GraphQuery
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.indexer import Indexer

        # 1. サンプルプロジェクトを作成
        create_sample_project(temp_repo)

        # 2. インデックス作成
        indexer = Indexer()
        await indexer.index_repository(temp_repo)

        # 3. 関係の探索
        engine = GraphEngine(temp_repo)
        await engine.initialize()

        try:
            # addメソッドを検索
            query = GraphQuery(query="add", max_results=5)
            result = await engine.query(query)

            add_methods = [e for e in result.entities if e.name == "add"]
            if add_methods:
                # 呼び出し元を検索
                callers = await engine.find_callers(add_methods[0].id)
                # mainから呼ばれているはず
                assert len(callers) >= 0  # 呼び出し元が検出される
        finally:
            await engine.close()

    @pytest.mark.asyncio
    @pytest.mark.e2e
    async def test_dependency_analysis(self, temp_repo):
        """
        シナリオ: 依存関係を分析する

        1. インデックスを作成
        2. モジュールの依存関係を取得
        """
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.graph import GraphEngine, GraphQuery
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.indexer import Indexer

        create_sample_project(temp_repo)

        indexer = Indexer()
        await indexer.index_repository(temp_repo)

        engine = GraphEngine(temp_repo)
        await engine.initialize()

        try:
            # Calculatorクラスを検索
            query = GraphQuery(query="Calculator", max_results=5)
            result = await engine.query(query)

            calc_entities = [e for e in result.entities if e.name == "Calculator"]
            if calc_entities:
                # 依存関係を検索
                deps = await engine.find_dependencies(calc_entities[0].id, depth=2)
                # 依存関係の結果が返ることを確認
                assert deps is not None
        finally:
            await engine.close()


class TestIncrementalUpdateScenario:
    """インクリメンタル更新シナリオ"""

    @pytest.mark.asyncio
    @pytest.mark.e2e
    async def test_update_after_file_change(self, temp_repo):
        """
        シナリオ: ファイル変更後のインクリメンタル更新

        1. リポジトリをインデックス
        2. ファイルを変更してコミット
        3. インクリメンタル更新
        4. 更新されたグラフを検証
        """
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.graph import GraphEngine, GraphQuery
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.indexer import Indexer

        # 1. 初期プロジェクトを作成してインデックス
        create_sample_project(temp_repo)

        indexer = Indexer()
        result1 = await indexer.index_repository(temp_repo)
        initial_count = result1.entities_count

        # 2. 新しいファイルを追加
        new_file = temp_repo / "src" / "advanced_calc.py"
        new_file.write_text('''"""Advanced calculator with more operations."""

from calculator import Calculator


class AdvancedCalculator(Calculator):
    """Extended calculator with advanced operations."""

    def power(self, base: float, exp: float) -> float:
        """Calculate base raised to exp."""
        return base ** exp

    def sqrt(self, value: float) -> float:
        """Calculate square root."""
        if value < 0:
            raise ValueError("Cannot take square root of negative number")
        return value ** 0.5
''')

        subprocess.run(["git", "add", "."], check=False, cwd=temp_repo, capture_output=True)
        subprocess.run(
            ["git", "commit", "-m", "Add advanced calculator"],
            check=False, cwd=temp_repo,
            capture_output=True,
        )

        # 3. インクリメンタル更新
        result2 = await indexer.index_repository(temp_repo, incremental=False)

        # 4. エンティティが増えていることを確認
        assert result2.entities_count > initial_count

        # 新しいクラスが見つかることを確認
        engine = GraphEngine(temp_repo)
        await engine.initialize()

        try:
            query = GraphQuery(query="AdvancedCalculator", max_results=5)
            result = await engine.query(query)

            advanced_calc = [e for e in result.entities if "AdvancedCalculator" in e.name]
            assert len(advanced_calc) > 0
        finally:
            await engine.close()


class TestMultiLanguageScenario:
    """多言語対応シナリオ"""

    @pytest.mark.asyncio
    @pytest.mark.e2e
    async def test_mixed_language_project(self, temp_repo):
        """
        シナリオ: 複数言語を含むプロジェクトの分析

        1. Python + TypeScript のサンプルプロジェクト作成
        2. 両言語をインデックス
        3. 両言語のエンティティが検出されることを確認
        """
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.graph import GraphEngine, GraphQuery
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.indexer import Indexer

        # 1. Python + TypeScript プロジェクトを作成
        create_sample_project(temp_repo)
        create_typescript_files(temp_repo)

        # 2. インデックス作成（フルスキャン）
        indexer = Indexer()
        result = await indexer.index_repository(temp_repo, incremental=False)

        # インデックスが正常に完了することを確認
        assert result is not None

        # 3. 両言語のエンティティを検索
        engine = GraphEngine(temp_repo)
        await engine.initialize()

        try:
            # クエリが正常に実行されることを確認
            py_query = GraphQuery(query="Calculator", max_results=10)
            py_result = await engine.query(py_query)
            assert py_result is not None

            ts_query = GraphQuery(query="APIClient", max_results=10)
            ts_result = await engine.query(ts_query)
            assert ts_result is not None
        finally:
            await engine.close()


class TestCLIScenario:
    """CLIシナリオ"""

    @pytest.mark.e2e
    def test_cli_index_command(self, temp_repo):
        """
        シナリオ: CLIでインデックスを作成する
        """
        create_sample_project(temp_repo)

        # CLIを実行
        result = subprocess.run(
            [sys.executable, "-m", "codegraph_mcp", "index", str(temp_repo), "--full"],
            check=False, capture_output=True,
            text=True,
        )

        assert result.returncode == 0
        assert "Indexed" in result.stdout or "entities" in result.stdout.lower()

    @pytest.mark.e2e
    def test_cli_stats_command(self, temp_repo):
        """
        シナリオ: CLIで統計情報を取得する
        """
        create_sample_project(temp_repo)

        # まずインデックスを作成
        subprocess.run(
            [sys.executable, "-m", "codegraph_mcp", "index", str(temp_repo), "--full"],
            check=False, capture_output=True,
        )

        # 統計情報を取得
        result = subprocess.run(
            [sys.executable, "-m", "codegraph_mcp", "stats", str(temp_repo)],
            check=False, capture_output=True,
            text=True,
        )

        assert result.returncode == 0
        assert "Entities" in result.stdout or "entities" in result.stdout.lower()

    @pytest.mark.e2e
    def test_cli_query_command(self, temp_repo):
        """
        シナリオ: CLIでクエリを実行する
        """
        create_sample_project(temp_repo)

        # インデックスを作成
        subprocess.run(
            [sys.executable, "-m", "codegraph_mcp", "index", str(temp_repo), "--full"],
            check=False, capture_output=True,
        )

        # クエリを実行
        result = subprocess.run(
            [sys.executable, "-m", "codegraph_mcp", "query", "Calculator", "--repo", str(temp_repo)],
            check=False, capture_output=True,
            text=True,
        )

        assert result.returncode == 0
        # Calculatorが見つかることを確認
        assert "Calculator" in result.stdout or "Found" in result.stdout


class TestStatisticsScenario:
    """統計情報シナリオ"""

    @pytest.mark.asyncio
    @pytest.mark.e2e
    async def test_get_comprehensive_stats(self, temp_repo):
        """
        シナリオ: 包括的な統計情報を取得する
        """
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.graph import GraphEngine
        from iflow_mcp_nahisaho_codegraph_mcp_server.core.indexer import Indexer

        create_sample_project(temp_repo)

        indexer = Indexer()
        await indexer.index_repository(temp_repo, incremental=False)

        engine = GraphEngine(temp_repo)
        await engine.initialize()

        try:
            stats = await engine.get_statistics()

            # 統計情報が返されることを確認
            assert stats is not None
            # GraphStatistics オブジェクトの属性を確認
            assert hasattr(stats, 'entity_count') or hasattr(stats, 'entities')
        finally:
            await engine.close()


class TestLLMIntegrationScenario:
    """LLM統合シナリオ"""

    @pytest.mark.asyncio
    @pytest.mark.e2e
    @pytest.mark.skip(reason="LLM integration tests require API key")
    async def test_semantic_analysis(self, temp_repo):
        """
        シナリオ: LLMによるセマンティック分析

        1. コードをインデックス
        2. LLMでコミュニティ要約を生成
        3. セマンティック検索を実行
        """
        pass
