"""Shared fixtures for integration tests that call real LLM APIs."""

import pytest
import pytest_asyncio

from dotpromptz.credentials import load_credentials

OPENAI_INTEGRATION_MODEL = 'gpt-5.2'
OPENAI_INTEGRATION_IMAGE_MODEL = 'gpt-image-1.5'
ANTHROPIC_INTEGRATION_MODEL = 'claude-haiku-4-5-20251001'
GOOGLE_INTEGRATION_MODEL = 'gemini-3.1-flash-image-preview'
GOOGLE_INTEGRATION_IMAGE_MODEL = 'gemini-3.1-flash-image-preview'


def _load_credentials_or_skip():
    """Load credentials pool or skip integration tests when unavailable."""
    try:
        return load_credentials()
    except Exception as exc:  # noqa: BLE001
        pytest.skip(f'Unable to load API credentials: {exc}')


def _is_rate_limit_error(exc: Exception) -> bool:
    """Return whether an exception likely indicates provider quota/rate limiting."""
    message = str(exc).lower()
    keywords = (
        '429',
        'rate limit',
        'resource exhausted',
        'quota',
        'too many requests',
    )
    return any(keyword in message for keyword in keywords)


def _wrap_generate_with_rate_limit_skip(adapter):
    """Wrap ``adapter.generate`` so provider rate limits skip integration tests."""
    original_generate = adapter.generate

    async def _wrapped_generate(*args, **kwargs):
        try:
            return await original_generate(*args, **kwargs)
        except Exception as exc:  # noqa: BLE001
            if _is_rate_limit_error(exc):
                pytest.skip(f'Skipped due provider rate limit/quota: {exc}')
            raise

    adapter.generate = _wrapped_generate
    return adapter


def pytest_configure(config: pytest.Config) -> None:
    """Register the ``integration`` marker."""
    config.addinivalue_line(
        'markers',
        'integration: tests that call real LLM APIs (require API keys)',
    )


@pytest.fixture(autouse=True)
def _skip_without_credentials(request) -> None:
    """Automatically skip integration tests when no matching credentials are available.

    Checks the API_CREDENTIALS pool for credentials matching the adapter
    required by each test's fixture.
    """
    pool = _load_credentials_or_skip()
    fixture_names = getattr(request, 'fixturenames', [])
    if 'anthropic_adapter' in fixture_names:
        if not pool.filter('anthropic', model=ANTHROPIC_INTEGRATION_MODEL):
            pytest.skip(f'No anthropic credentials support model {ANTHROPIC_INTEGRATION_MODEL!r}')
    elif 'google_image_adapter' in fixture_names:
        if not pool.filter('google', model=GOOGLE_INTEGRATION_IMAGE_MODEL):
            pytest.skip(f'No google credentials support model {GOOGLE_INTEGRATION_IMAGE_MODEL!r}')
    elif 'google_adapter' in fixture_names:
        if not pool.filter('google', model=GOOGLE_INTEGRATION_MODEL):
            pytest.skip(f'No google credentials support model {GOOGLE_INTEGRATION_MODEL!r}')
    elif 'openai_image_adapter' in fixture_names:
        if not pool.filter('openai', model=OPENAI_INTEGRATION_IMAGE_MODEL):
            pytest.skip(f'No openai credentials support model {OPENAI_INTEGRATION_IMAGE_MODEL!r}')
    else:
        if not pool.filter('openai', model=OPENAI_INTEGRATION_MODEL):
            pytest.skip(f'No openai credentials support model {OPENAI_INTEGRATION_MODEL!r}')


@pytest_asyncio.fixture()
async def openai_adapter():
    """Create an OpenAIAdapter with credentials from the pool."""
    from dotpromptz.adapters.openai import OpenAIAdapter

    pool = _load_credentials_or_skip()
    try:
        cred = pool.next('openai', model=OPENAI_INTEGRATION_MODEL)
    except ValueError:
        pytest.skip(f'No openai credentials support model {OPENAI_INTEGRATION_MODEL!r}')
    adapter = OpenAIAdapter(credential=cred, default_model=OPENAI_INTEGRATION_MODEL)
    adapter = _wrap_generate_with_rate_limit_skip(adapter)
    yield adapter
    await adapter.aclose()


@pytest_asyncio.fixture()
async def anthropic_adapter():
    """Create an AnthropicAdapter with credentials from the pool."""
    from dotpromptz.adapters.anthropic import AnthropicAdapter

    pool = _load_credentials_or_skip()
    try:
        cred = pool.next('anthropic', model=ANTHROPIC_INTEGRATION_MODEL)
    except ValueError:
        pytest.skip(f'No anthropic credentials support model {ANTHROPIC_INTEGRATION_MODEL!r}')
    adapter = AnthropicAdapter(credential=cred, default_model=ANTHROPIC_INTEGRATION_MODEL)
    adapter = _wrap_generate_with_rate_limit_skip(adapter)
    yield adapter
    await adapter.aclose()


@pytest_asyncio.fixture()
async def openai_image_adapter():
    """Create an OpenAIAdapter using an image-generation model."""
    from dotpromptz.adapters.openai import OpenAIAdapter

    pool = _load_credentials_or_skip()
    try:
        cred = pool.next('openai', model=OPENAI_INTEGRATION_IMAGE_MODEL)
    except ValueError:
        pytest.skip(f'No openai credentials support model {OPENAI_INTEGRATION_IMAGE_MODEL!r}')
    adapter = OpenAIAdapter(credential=cred, default_model=OPENAI_INTEGRATION_IMAGE_MODEL)
    adapter = _wrap_generate_with_rate_limit_skip(adapter)
    yield adapter
    await adapter.aclose()


@pytest_asyncio.fixture()
async def google_adapter():
    """Create a GoogleAdapter with credentials from the pool."""
    from dotpromptz.adapters.google import GoogleAdapter

    pool = _load_credentials_or_skip()
    try:
        cred = pool.next('google', model=GOOGLE_INTEGRATION_MODEL)
    except ValueError:
        pytest.skip(f'No google credentials support model {GOOGLE_INTEGRATION_MODEL!r}')
    adapter = GoogleAdapter(credential=cred, default_model=GOOGLE_INTEGRATION_MODEL)
    adapter = _wrap_generate_with_rate_limit_skip(adapter)
    yield adapter
    await adapter.aclose()


@pytest_asyncio.fixture()
async def google_image_adapter():
    """Create a GoogleAdapter using an image-generation model."""
    from dotpromptz.adapters.google import GoogleAdapter

    pool = _load_credentials_or_skip()
    try:
        cred = pool.next('google', model=GOOGLE_INTEGRATION_IMAGE_MODEL)
    except ValueError:
        pytest.skip(f'No google credentials support model {GOOGLE_INTEGRATION_IMAGE_MODEL!r}')
    adapter = GoogleAdapter(credential=cred, default_model=GOOGLE_INTEGRATION_IMAGE_MODEL)
    adapter = _wrap_generate_with_rate_limit_skip(adapter)
    yield adapter
    await adapter.aclose()


@pytest.fixture()
def dotprompt_instance():
    """Create a lightweight renderer compatible with integration templates."""
    from dotpromptz.compiler import render_prompt

    def role(name: str) -> str:
        allowed = {'system', 'user', 'model', 'image'}
        if name not in allowed:
            raise ValueError(f'Invalid role helper value: {name!r}')
        return f':::{name}\n'

    class _Renderer:
        def render(self, source: str, *, input: dict | None = None):
            return render_prompt(
                source,
                input=input,
                default_model=OPENAI_INTEGRATION_MODEL,
                helpers={'role': role},
            )

    return _Renderer()
