"""
Provider integration test configuration.

Provides fixtures and markers for testing real AI provider invocations.
Tests are skipped by default unless explicitly enabled via markers or environment.

Usage:
    # Run all provider tests (requires all providers available)
    pytest tests/integration/providers/ -m live_providers

    # Run specific provider tests
    pytest tests/integration/providers/ -m gemini
    pytest tests/integration/providers/ -m codex
    pytest tests/integration/providers/ -m claude

    # Run smoke tests only (quick availability check)
    pytest tests/integration/providers/ -m smoke

    # Run workflow tests only
    pytest tests/integration/providers/ -m plan_review
    pytest tests/integration/providers/ -m fidelity_review
"""

import json
from pathlib import Path
from typing import Any, Dict, Optional

import pytest

from foundry_mcp.core.providers import (
    ProviderRequest,
    detect_provider_availability,
)

# =============================================================================
# Marker Registration
# =============================================================================


def pytest_configure(config):
    """Register custom markers for provider tests."""
    # Provider-specific markers
    config.addinivalue_line("markers", "live_providers: tests that invoke real AI providers")
    config.addinivalue_line("markers", "gemini: tests requiring gemini CLI")
    config.addinivalue_line("markers", "codex: tests requiring codex CLI")
    config.addinivalue_line("markers", "claude: tests requiring claude CLI")
    config.addinivalue_line("markers", "cursor_agent: tests requiring cursor-agent CLI")
    config.addinivalue_line("markers", "opencode: tests requiring opencode CLI")

    # Test category markers
    config.addinivalue_line("markers", "smoke: quick provider availability tests")
    config.addinivalue_line("markers", "plan_review: plan review workflow tests")
    config.addinivalue_line("markers", "fidelity_review: fidelity review workflow tests")
    config.addinivalue_line("markers", "slow: tests that may take >30 seconds")
    config.addinivalue_line("markers", "synthesis: multi-model synthesis workflow tests")
    config.addinivalue_line("markers", "router_smoke: router-level smoke tests with real providers")
    config.addinivalue_line("markers", "plan_synthesis: plan review synthesis tests")
    config.addinivalue_line("markers", "fidelity_synthesis: fidelity review synthesis tests")


# =============================================================================
# Skip Logic
# =============================================================================


def pytest_collection_modifyitems(config, items):
    """Skip provider tests if the provider CLI is not available or not configured."""
    import os

    provider_markers = {"gemini", "codex", "claude", "cursor_agent", "opencode"}

    # Check if live provider tests are explicitly enabled
    live_tests_enabled = os.environ.get("FOUNDRY_ENABLE_LIVE_PROVIDER_TESTS", "").lower() in ("1", "true", "yes")

    for item in items:
        item_markers = {m.name for m in item.iter_markers()}

        # Skip all live_providers tests unless explicitly enabled
        if "live_providers" in item_markers and not live_tests_enabled:
            item.add_marker(
                pytest.mark.skip(
                    reason="Live provider tests disabled (set FOUNDRY_ENABLE_LIVE_PROVIDER_TESTS=1 to enable)"
                )
            )
            continue

        # Check provider availability for specific provider tests
        for provider in provider_markers:
            if provider in item_markers:
                provider_id = provider.replace("_", "-")  # cursor_agent -> cursor-agent
                if not detect_provider_availability(provider_id):
                    item.add_marker(pytest.mark.skip(reason=f"Provider '{provider_id}' not available"))


# =============================================================================
# Fixtures - Simple Test Prompts
# =============================================================================

SIMPLE_PROMPT = "Reply with exactly: PONG"

SIMPLE_PLAN_REVIEW_PROMPT = """Review this simple plan and provide brief feedback:

# Plan: Add greeting function
1. Create greet(name) function
2. Return "Hello, {name}!"
3. Add tests

Respond with a JSON object containing:
- "feasibility": "high" or "medium" or "low"
- "issues": list of strings (can be empty)
- "recommendation": "approve" or "revise"
"""

SIMPLE_FIDELITY_PROMPT = """Check if this implementation matches the spec:

SPEC: Function greet(name) returns "Hello, {name}!"
IMPLEMENTATION: def greet(name): return f"Hello, {name}!"

Respond with a JSON object containing:
- "compliant": true or false
- "deviations": list of strings (can be empty)
"""


@pytest.fixture
def simple_prompt() -> str:
    """Minimal prompt for smoke testing - expects 'PONG' response."""
    return SIMPLE_PROMPT


@pytest.fixture
def simple_plan_review_prompt() -> str:
    """Simple plan review prompt with expected JSON response structure."""
    return SIMPLE_PLAN_REVIEW_PROMPT


@pytest.fixture
def simple_fidelity_prompt() -> str:
    """Simple fidelity check prompt with expected JSON response structure."""
    return SIMPLE_FIDELITY_PROMPT


# =============================================================================
# Fixtures - File-based Fixtures
# =============================================================================


@pytest.fixture
def fixtures_dir() -> Path:
    """Path to the fixtures directory."""
    return Path(__file__).parent / "fixtures"


@pytest.fixture
def simple_plan_md(fixtures_dir: Path) -> str:
    """Load simple_plan.md fixture content."""
    return (fixtures_dir / "simple_plan.md").read_text()


@pytest.fixture
def simple_spec_json(fixtures_dir: Path) -> Dict[str, Any]:
    """Load simple_spec.json fixture as dict."""
    return json.loads((fixtures_dir / "simple_spec.json").read_text())


# =============================================================================
# Fixtures - Provider Helpers
# =============================================================================


@pytest.fixture
def provider_request_factory():
    """Factory for creating ProviderRequest objects.

    Note: temperature and max_tokens default to None to avoid issues
    with providers that don't support these parameters (e.g., codex, claude CLI).
    If max_tokens is needed, use 4096 as a reasonable default.
    """

    def _create(
        prompt: str,
        model: Optional[str] = None,
        timeout: float = 60.0,
        temperature: Optional[float] = None,
        max_tokens: Optional[int] = None,
    ) -> ProviderRequest:
        return ProviderRequest(
            prompt=prompt,
            model=model,
            timeout=timeout,
            temperature=temperature,
            max_tokens=max_tokens,
        )

    return _create


@pytest.fixture
def available_providers_list() -> list:
    """List of currently available providers."""
    providers = ["gemini", "codex", "claude", "cursor-agent", "opencode"]
    return [p for p in providers if detect_provider_availability(p)]


# =============================================================================
# Fixtures - Result Validation
# =============================================================================


@pytest.fixture
def validate_provider_result():
    """Validator for ProviderResult objects."""

    def _validate(result, expect_content: bool = True):
        from foundry_mcp.core.providers import ProviderResult, ProviderStatus

        assert isinstance(result, ProviderResult), f"Expected ProviderResult, got {type(result)}"
        assert result.status in ProviderStatus, f"Invalid status: {result.status}"

        if expect_content:
            assert result.status == ProviderStatus.SUCCESS, f"Expected SUCCESS, got {result.status}"
            assert result.content, "Expected non-empty content"
            assert isinstance(result.content, str), f"Content should be str, got {type(result.content)}"

        return result

    return _validate


@pytest.fixture
def validate_json_response():
    """Validator for JSON responses from providers."""

    def _validate(content: str, required_keys: Optional[list] = None) -> Dict[str, Any]:
        try:
            data = json.loads(content)
        except json.JSONDecodeError as e:
            # Try to extract JSON from markdown code blocks
            import re

            match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", content, re.DOTALL)
            if match:
                data = json.loads(match.group(1))
            else:
                raise AssertionError(f"Response is not valid JSON: {e}\nContent: {content[:500]}") from e

        if required_keys:
            missing = set(required_keys) - set(data.keys())
            assert not missing, f"Response missing required keys: {missing}"

        return data

    return _validate
