"""Integration tests for Cleave probe module.

Tests cover:
- Real Python project probing (using cleave itself as test subject)
- Stack detection accuracy
- Relevant file discovery
- Question generation based on context
- Pattern matching integration
"""

import tempfile
from pathlib import Path

import pytest

from cleave.core.probe import (
    ProbeResult,
    detect_stack,
    find_relevant_files,
    generate_probe_questions,
    probe_codebase,
)


class TestRealProjectProbe:
    """Tests using cleave codebase as real test subject."""

    def test_probe_cleave_codebase(self):
        """Should successfully probe the cleave codebase itself."""
        # Use the cleave project root as test subject
        project_root = Path(__file__).parent.parent
        directive = "Add integration tests for the TUI module"

        result = probe_codebase(directive, project_root)

        # Should detect stack
        assert isinstance(result, ProbeResult)
        assert result.detected_stack is not None
        assert result.detected_stack.get("language") == "python"

        # Should find relevant files
        assert len(result.relevant_files) > 0

        # Should match a pattern
        assert result.pattern_match is not None or result.pattern_confidence >= 0

    def test_probe_detects_python_stack(self):
        """Should detect Python stack from cleave codebase."""
        project_root = Path(__file__).parent.parent
        stack = detect_stack(project_root)

        assert stack.get("language") == "python"
        # Should detect pyproject.toml
        assert (project_root / "pyproject.toml").exists()

    def test_probe_finds_test_files(self):
        """Should find test files when directive mentions testing."""
        project_root = Path(__file__).parent.parent
        directive = "Fix failing tests in test_probe.py"

        files = find_relevant_files(project_root, directive, max_files=20)

        # Should find test-related files
        test_files = [f for f in files if "test" in f.lower()]
        assert len(test_files) > 0

        # Should specifically find probe-related files
        probe_files = [f for f in files if "probe" in f.lower()]
        assert len(probe_files) > 0

    def test_probe_finds_tui_files(self):
        """Should find TUI files when directive mentions TUI."""
        project_root = Path(__file__).parent.parent
        directive = "Refactor the TUI chat panel widget"

        files = find_relevant_files(project_root, directive, max_files=20)

        # Should find TUI-related files
        tui_files = [f for f in files if "tui" in f.lower()]
        assert len(tui_files) > 0


class TestStackDetection:
    """Tests for stack detection accuracy."""

    def test_detect_python_with_pyproject(self):
        """Should detect Python from pyproject.toml."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "pyproject.toml").write_text("[project]\nname='test'")

            stack = detect_stack(project)
            assert stack.get("language") == "python"

    def test_detect_python_with_requirements(self):
        """Should detect Python from requirements.txt."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "requirements.txt").write_text("requests==2.31.0")

            stack = detect_stack(project)
            assert stack.get("language") == "python"

    def test_detect_typescript(self):
        """Should detect TypeScript from tsconfig.json."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "tsconfig.json").write_text('{"compilerOptions":{}}')
            (project / "app.ts").write_text("const x: string = 'hello';")

            stack = detect_stack(project)
            assert stack.get("language") == "typescript"

    def test_detect_javascript(self):
        """Should detect JavaScript from package.json."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "package.json").write_text('{"name":"test"}')

            stack = detect_stack(project)
            assert stack.get("language") == "javascript"

    def test_detect_docker_infrastructure(self):
        """Should detect Docker in infrastructure."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "Dockerfile").write_text("FROM python:3.11")

            stack = detect_stack(project)
            assert stack.get("infrastructure") == "docker"

    def test_detect_kubernetes_infrastructure(self):
        """Should detect Kubernetes from k8s directory."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            k8s_dir = project / "k8s"
            k8s_dir.mkdir()
            (k8s_dir / "deployment.yaml").write_text("apiVersion: apps/v1")

            stack = detect_stack(project)
            assert stack.get("infrastructure") == "kubernetes"

    def test_detect_no_stack_in_empty_directory(self):
        """Should return None values for empty directories."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            stack = detect_stack(project)

            # All categories should be None
            for category, value in stack.items():
                assert value is None


class TestRelevantFileDiscovery:
    """Tests for relevant file discovery based on directive."""

    def test_find_auth_files(self):
        """Should find auth-related files when directive mentions auth."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "auth.py").write_text("# auth module")
            (project / "users.py").write_text("# user management")
            (project / "models.py").write_text("# database models")
            (project / "unrelated.py").write_text("# unrelated")

            directive = "Implement user authentication with JWT tokens"
            files = find_relevant_files(project, directive, max_files=10)

            # Should find auth and user files
            file_names = [Path(f).name for f in files]
            assert "auth.py" in file_names
            assert "users.py" in file_names

    def test_find_api_files(self):
        """Should find API-related files when directive mentions API."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            api_dir = project / "api"
            api_dir.mkdir()
            (api_dir / "routes.py").write_text("# API routes")
            (api_dir / "handlers.py").write_text("# request handlers")
            (project / "main.py").write_text("# application entry")

            directive = "Add new API endpoint for user registration"
            files = find_relevant_files(project, directive, max_files=10)

            # Should prioritize API-related files
            file_paths = [str(f) for f in files]
            api_files = [f for f in file_paths if "api" in f]
            assert len(api_files) > 0

    def test_find_database_files(self):
        """Should find database files when directive mentions database."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "models.py").write_text("# ORM models")
            (project / "migrations").mkdir()
            (project / "migrations" / "001_initial.py").write_text("# migration")
            (project / "db.py").write_text("# database config")

            directive = "Add database migration for new user table"
            files = find_relevant_files(project, directive, max_files=10)

            # Should find db-related files (match on full path, not just basename)
            assert any("model" in f or "db" in f or "migration" in f for f in files)

    def test_max_files_limit(self):
        """Should respect max_files parameter."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            # Create many matching files
            for i in range(20):
                (project / f"test_{i}.py").write_text("# test file")

            directive = "Fix test failures"
            files = find_relevant_files(project, directive, max_files=5)

            assert len(files) <= 5

    def test_prefers_source_files_over_others(self):
        """Should prioritize .py files over other file types."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "auth.py").write_text("# source")
            (project / "auth.md").write_text("# docs")
            (project / "auth.txt").write_text("# notes")

            directive = "Update auth module"
            files = find_relevant_files(project, directive, max_files=10)

            # .py file should be prioritized
            if len(files) > 0:
                assert Path(files[0]).suffix == ".py"


class TestQuestionGeneration:
    """Tests for context-aware question generation."""

    def test_generate_questions_for_directive(self):
        """Should generate questions based on directive content."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "pyproject.toml").write_text("[project]\nname='test'")

            directive = "Add user authentication system"
            questions = generate_probe_questions(directive, project)

            # Should generate some questions
            assert isinstance(questions, list)
            # Questions are pattern-dependent, may be empty for generic directive

    def test_questions_triggered_by_file_exists(self):
        """Should trigger questions when specific files exist."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "Dockerfile").write_text("FROM python:3.11")

            directive = "Deploy application to production"
            questions = generate_probe_questions(directive, project)

            # Docker presence may trigger deployment questions
            # (pattern-dependent)
            assert isinstance(questions, list)

    def test_questions_triggered_by_file_missing(self):
        """Should trigger questions when expected files are missing."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            # Empty project

            directive = "Set up CI/CD pipeline"
            questions = generate_probe_questions(directive, project)

            # Missing CI files may trigger questions
            assert isinstance(questions, list)

    def test_questions_triggered_by_keywords(self):
        """Should trigger questions based on directive keywords."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "pyproject.toml").write_text("[project]\nname='test'")

            directive = "Implement authentication with OAuth2 and JWT"
            questions = generate_probe_questions(directive, project)

            # Auth keywords should trigger relevant questions
            assert isinstance(questions, list)


class TestPatternMatchingIntegration:
    """Tests for pattern matching integration with probe."""

    def test_probe_matches_new_feature_pattern(self):
        """Should match 'new feature' pattern for feature directives."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "pyproject.toml").write_text("[project]\nname='test'")

            directive = "Add new user profile feature with avatar upload"
            result = probe_codebase(directive, project)

            # Should match a pattern (likely "new_feature")
            # Exact match depends on pattern library
            assert isinstance(result.pattern_confidence, float)

    def test_probe_matches_bug_fix_pattern(self):
        """Should match bug fix patterns for fix directives."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "pyproject.toml").write_text("[project]\nname='test'")

            directive = "Fix memory leak in background task processor"
            result = probe_codebase(directive, project)

            # Should attempt pattern matching
            assert isinstance(result.pattern_confidence, float)

    def test_probe_extracts_directive_keywords(self):
        """Should extract relevant keywords from directive."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "pyproject.toml").write_text("[project]\nname='test'")

            directive = "Add API authentication using JWT tokens"
            result = probe_codebase(directive, project)

            # Should extract keywords
            assert isinstance(result.directive_keywords, list)
            # May include: "api", "auth", etc.

    def test_probe_result_format_consistency(self):
        """ProbeResult should have consistent structure."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "pyproject.toml").write_text("[project]\nname='test'")

            directive = "Generic task"
            result = probe_codebase(directive, project)

            # Verify all expected fields exist
            assert hasattr(result, "detected_stack")
            assert hasattr(result, "relevant_files")
            assert hasattr(result, "triggered_questions")
            assert hasattr(result, "pattern_match")
            assert hasattr(result, "pattern_confidence")
            assert hasattr(result, "directive_keywords")

            # Fields should have correct types
            assert isinstance(result.detected_stack, dict)
            assert isinstance(result.relevant_files, list)
            assert isinstance(result.triggered_questions, list)
            assert isinstance(result.pattern_confidence, float)


class TestProbeEdgeCases:
    """Edge cases and error handling for probe."""

    def test_probe_nonexistent_directory(self):
        """Should handle non-existent directories gracefully."""
        nonexistent = Path("/nonexistent/path/that/does/not/exist")
        directive = "Do something"

        result = probe_codebase(directive, nonexistent)

        # Should return empty result, not crash
        assert isinstance(result, ProbeResult)
        assert len(result.relevant_files) == 0

    def test_probe_empty_directive(self):
        """Should handle empty directive."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)

            directive = ""
            result = probe_codebase(directive, project)

            # Should not crash
            assert isinstance(result, ProbeResult)

    def test_detect_stack_permission_denied(self):
        """Should handle directories with limited permissions gracefully."""
        # This test is platform-dependent and may be skipped
        # Just ensure it doesn't crash on the current directory
        result = detect_stack(Path("."))
        assert isinstance(result, dict)

    def test_find_relevant_files_with_special_characters(self):
        """Should handle files with special characters in names."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            # Create files with special characters
            (project / "test-file.py").write_text("# test")
            (project / "test_file_2.py").write_text("# test")

            directive = "Update test files"
            files = find_relevant_files(project, directive, max_files=10)

            # Should find files without crashing
            assert isinstance(files, list)

    def test_probe_with_symlinks(self):
        """Should handle symbolic links without infinite loops."""
        with tempfile.TemporaryDirectory() as tmpdir:
            project = Path(tmpdir)
            (project / "real_dir").mkdir()
            (project / "real_dir" / "file.py").write_text("# code")

            # Create symlink (skip on Windows if not supported)
            try:
                (project / "link_dir").symlink_to(project / "real_dir")
            except (OSError, NotImplementedError):
                pytest.skip("Symlinks not supported on this platform")

            directive = "Update code"
            result = probe_codebase(directive, project)

            # Should complete without infinite loop
            assert isinstance(result, ProbeResult)
