"""F.X.fix #11 — OpenRouter pricing table for cost_usd attribution."""

from __future__ import annotations

import base64
import json
from unittest.mock import AsyncMock, patch

import pytest

from plugins.ai_image.providers.base import GenerationRequest
from plugins.ai_image.providers.openrouter import (
    _MODEL_PRICING,
    OpenRouterProvider,
    _cost_for,
    _pricing_table,
)

_PNG_1x1 = base64.b64decode(
    b"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
)


def _fake_resp(*, status, json_data=None, text_data="", raw=None, headers=None):
    resp = AsyncMock()
    resp.status = status
    resp.text = AsyncMock(return_value=text_data or "")
    if json_data is not None:
        resp.json = AsyncMock(return_value=json_data)
    if raw is not None:
        resp.read = AsyncMock(return_value=raw)
    resp.headers = headers or {}
    return resp


def _mock_session(responses):
    iterator = iter(responses)

    def _request(*_a, **_kw):
        resp = next(iterator)
        return AsyncMock(
            __aenter__=AsyncMock(return_value=resp),
            __aexit__=AsyncMock(return_value=False),
        )

    sess = AsyncMock()
    sess.post = _request
    sess.get = _request
    return sess


class TestPricingTable:
    def test_known_model_maps_to_non_null_cost(self):
        assert _cost_for("google/gemini-2.5-flash-image") is not None
        assert _cost_for("google/gemini-2.5-flash-image") > 0

    def test_unknown_model_returns_none(self, caplog):
        assert _cost_for("nonexistent/model-xyz") is None

    def test_gemini_preview_still_priced(self):
        # Preview alias is deprecated but still has a price entry so if
        # an admin call bypasses the generate-time guard the cost column
        # is still usable for audit.
        assert _cost_for("google/gemini-2.5-flash-image-preview") is not None

    def test_pricing_table_contains_expected_providers(self):
        ids = set(_MODEL_PRICING.keys())
        assert any(m.startswith("google/gemini-") for m in ids)
        assert any(m.startswith("openai/dall-e") for m in ids)
        assert any(m.startswith("black-forest-labs/flux") for m in ids)

    def test_env_override_merges_and_wins(self, monkeypatch):
        monkeypatch.setenv(
            "OPENROUTER_PRICING_OVERRIDE",
            json.dumps({"google/gemini-2.5-flash-image": 0.001, "custom/model": 0.5}),
        )
        table = _pricing_table()
        assert table["google/gemini-2.5-flash-image"] == 0.001
        assert table["custom/model"] == 0.5
        # Untouched entries stay from the hard-coded table.
        assert "openai/dall-e-3" in table

    def test_env_override_bad_json_is_ignored(self, monkeypatch):
        monkeypatch.setenv("OPENROUTER_PRICING_OVERRIDE", "not-json{")
        # Falls back to the default table, no exception.
        assert (
            _pricing_table()["google/gemini-2.5-flash-image"]
            == _MODEL_PRICING["google/gemini-2.5-flash-image"]
        )


class TestGenerateAttributesCost:
    @pytest.mark.asyncio
    async def test_cost_usd_populated_for_known_model(self):
        provider = OpenRouterProvider()
        b64 = base64.b64encode(_PNG_1x1).decode()
        ok = _fake_resp(
            status=200,
            json_data={
                "choices": [
                    {
                        "message": {
                            "images": [{"image_url": {"url": f"data:image/png;base64,{b64}"}}]
                        }
                    }
                ]
            },
        )
        sess = _mock_session([ok])
        with patch("plugins.ai_image.providers.openrouter.aiohttp.ClientSession") as cls:
            cls.return_value = AsyncMock(
                __aenter__=AsyncMock(return_value=sess),
                __aexit__=AsyncMock(return_value=False),
            )
            result = await provider.generate("k", GenerationRequest(prompt="x"))
        assert result.cost_usd == _MODEL_PRICING["google/gemini-2.5-flash-image"]

    @pytest.mark.asyncio
    async def test_cost_usd_null_for_unknown_model(self):
        provider = OpenRouterProvider()
        b64 = base64.b64encode(_PNG_1x1).decode()
        ok = _fake_resp(
            status=200,
            json_data={
                "choices": [
                    {
                        "message": {
                            "images": [{"image_url": {"url": f"data:image/png;base64,{b64}"}}]
                        }
                    }
                ]
            },
        )
        sess = _mock_session([ok])
        with patch("plugins.ai_image.providers.openrouter.aiohttp.ClientSession") as cls:
            cls.return_value = AsyncMock(
                __aenter__=AsyncMock(return_value=sess),
                __aexit__=AsyncMock(return_value=False),
            )
            result = await provider.generate(
                "k", GenerationRequest(prompt="x", model="weird/unreleased-model")
            )
        assert result.cost_usd is None


class TestDeprecatedModelGuard:
    @pytest.mark.asyncio
    async def test_preview_model_silently_substitutes_to_ga(self, caplog):
        """Issue #90 — deprecated model id is auto-rewritten to the GA alias."""
        provider = OpenRouterProvider()
        b64 = base64.b64encode(_PNG_1x1).decode()
        ok = _fake_resp(
            status=200,
            json_data={
                "choices": [
                    {
                        "message": {
                            "images": [{"image_url": {"url": f"data:image/png;base64,{b64}"}}]
                        }
                    }
                ]
            },
        )
        sess = _mock_session([ok])
        with patch("plugins.ai_image.providers.openrouter.aiohttp.ClientSession") as cls:
            cls.return_value = AsyncMock(
                __aenter__=AsyncMock(return_value=sess),
                __aexit__=AsyncMock(return_value=False),
            )
            with caplog.at_level("WARNING", logger="mcphub.ai_image.openrouter"):
                result = await provider.generate(
                    "k",
                    GenerationRequest(prompt="x", model="google/gemini-2.5-flash-image-preview"),
                )
        assert result.meta["model"] == "google/gemini-2.5-flash-image"
        assert any(
            "is deprecated" in rec.message and "google/gemini-2.5-flash-image" in rec.message
            for rec in caplog.records
        )
