"""Unit tests for each provider adapter (via mocks in test mode)."""

from __future__ import annotations

import sys
from pathlib import Path

import pytest

_RECOIL = Path(__file__).resolve().parents[2]
if str(_RECOIL) not in sys.path:
    sys.path.insert(0, str(_RECOIL))


@pytest.fixture(autouse=True)
def _test_mode(monkeypatch):
    monkeypatch.setenv("RECOIL_PROVIDER_MODE", "test")
    monkeypatch.setenv("FAL_KEY", "x")
    monkeypatch.setenv("ATLAS_CLOUD_API_KEY", "x")
    monkeypatch.setenv("PIAPI_API_KEY", "x")
    from recoil.execution.providers.registry import reset_caches_for_tests

    reset_caches_for_tests()
    yield
    reset_caches_for_tests()


def _ufv(**k):
    from recoil.execution.providers.base import UnifiedVideoPayload

    kwargs = dict(
        prompt="hello", duration_s=5, resolution="720p", model_id="seeddance-2.0"
    )
    kwargs.update(k)
    return UnifiedVideoPayload(**kwargs)


def test_fal_adapter_submit_shape():
    from recoil.execution.providers.testing.mock_fal import ADAPTER

    req = ADAPTER.build_submit(_ufv(), tier="standard_720p")
    assert req.method == "POST"
    assert "submit" in req.url
    assert req.body["tier"] == "standard_720p"


def test_fal_adapter_poll_parsed_completed():
    from recoil.execution.providers.testing.mock_fal import ADAPTER

    pj = ADAPTER.parse_submit({"request_id": "r1"}, _ufv(), tier="standard_720p")
    result = ADAPTER.parse_poll({"status": "COMPLETED"}, pj)
    assert result.status == "COMPLETED"


def test_atlas_adapter_failed_poll_has_error():
    from recoil.execution.providers.testing.mock_atlas import ADAPTER

    pj = ADAPTER.parse_submit({"data": {"id": "a1"}}, _ufv(), tier="pro_720p")
    r = ADAPTER.parse_poll({"data": {"status": "failed", "message": "bad"}}, pj)
    assert r.status == "FAILED"
    assert "bad" in r.error


def test_piapi_adapter_populates_observed_cost():
    from recoil.execution.providers.testing.mock_piapi import ADAPTER

    pj = ADAPTER.parse_submit({"data": {"task_id": "t1"}}, _ufv(), tier="seedance-2")
    resp = {
        "data": {
            "status": "completed",
            "output": {"video": {"url": "https://cdn/x.mp4"}},
            "meta": {"usage": {"consume": 65.0}},
        }
    }
    r = ADAPTER.parse_poll(resp, pj)
    assert r.status == "COMPLETED"
    assert r.video_url.endswith(".mp4")
    assert r.observed_cost == pytest.approx(0.65, rel=1e-6)


def test_piapi_capabilities_rejects_end_frame():
    from recoil.execution.providers.testing.mock_piapi import ADAPTER

    assert ADAPTER.capabilities["end_frame"] is False


def test_all_adapters_support_model_id():
    """Each adapter must declare at least one supported model ID.

    CP-2 Phase 2 (2026-04-26): pre-CP-2 this test asserted every adapter
    supported "seeddance-2.0" specifically — true when fal/atlas/piapi
    were the only providers. The google adapter (nbp/flash/veo-3.1)
    breaks that assumption by design. Updated to assert the more
    general invariant: every adapter declares at least one model.
    """
    from recoil.execution.providers.registry import list_adapters

    for a in list_adapters():
        assert a.supported_models, f"{a.provider_id} declares no supported_models"


def test_compute_cost_scales_linearly_with_duration():
    from recoil.execution.providers.testing.mock_piapi import ADAPTER

    one = ADAPTER.compute_cost(5, "seedance-2", {})
    two = ADAPTER.compute_cost(10, "seedance-2", {})
    assert two == pytest.approx(2 * one)


def test_flora_rejects_over_cap_video_prompt(monkeypatch):
    """Flora blanks (not truncates) video prompts over 2500 chars — the
    adapter must fail loud before billing a promptless run (REC-123)."""
    import pytest as _pytest
    from recoil.execution.providers.flora import ADAPTER

    monkeypatch.setenv("FLORA_API_KEY", "ak_test")
    monkeypatch.setenv("RECOIL_FLORA_WORKSPACE", "ws_test")
    monkeypatch.setenv("RECOIL_FLORA_PROJECT", "prj_test")

    payload = _ufv(prompt="x. " * 1000)  # 3000 chars
    with _pytest.raises(ValueError, match="2500"):
        ADAPTER.build_submit(payload, tier="standard_720p")


def test_flora_failed_poll_surfaces_error_code(monkeypatch):
    """Flora failures carry error_code (not error_message); the adapter must
    surface it so take records show the real reason (REC-122 — runs failed
    with BILLING_NOT_ENOUGH_CREDITS but takes recorded only the generic
    'provider reported FAILED')."""
    from recoil.execution.providers.flora import ADAPTER
    from recoil.execution.providers.base import ProviderJob

    pj = ProviderJob(
        provider_id="flora", model_id="seeddance-2.0", native_id="run_x",
        tier="standard", duration_s=15, resolution="720p",
        native_state={"charged_cost": 0.0},
    )
    r = ADAPTER.parse_poll(
        {"status": "failed", "error_code": "BILLING_NOT_ENOUGH_CREDITS"}, pj
    )
    assert r.status == "FAILED"
    assert "BILLING_NOT_ENOUGH_CREDITS" in (r.error or "")
