import logging
from types import SimpleNamespace

import pytest

from recoil.execution import assembler
from recoil.execution.assembler import KeyframeRefBundle, compile_keyframe_parts


class _FakePart:
    @staticmethod
    def from_bytes(data, mime_type):
        return ("bytes", data, mime_type)

    @staticmethod
    def from_text(text):
        return ("text", text)


@pytest.fixture
def fake_genai(monkeypatch):
    monkeypatch.setattr(assembler, "_HAS_GENAI", True)
    monkeypatch.setattr(assembler, "genai_types", SimpleNamespace(Part=_FakePart))


def test_compile_keyframe_parts_missing_identity_ref_blocks(tmp_path, fake_genai):
    missing_identity = tmp_path / "missing_identity.png"
    bundle = KeyframeRefBundle(
        prompt="Render Kane.",
        model="gemini",
        identity_refs=[missing_identity],
    )

    with pytest.raises(ValueError, match=str(missing_identity)):
        compile_keyframe_parts(bundle)


def test_compile_keyframe_parts_missing_non_identity_refs_warn(
    tmp_path,
    caplog,
    fake_genai,
):
    missing_scene = tmp_path / "missing_scene.png"
    missing_pose = tmp_path / "missing_pose.png"
    missing_expression = tmp_path / "missing_expression.png"
    bundle = KeyframeRefBundle(
        prompt="Render the shot.",
        model="gemini",
        scene_ref=missing_scene,
        pose_ref=missing_pose,
        expression_refs=[missing_expression],
    )

    with caplog.at_level(logging.WARNING, logger="recoil.execution.assembler"):
        parts = compile_keyframe_parts(bundle)

    assert parts == [("text", "Render the shot.")]
    messages = [
        record.message
        for record in caplog.records
        if "FALLBACK_FIRED keyframe_ref_missing" in record.message
    ]
    assert messages == [
        f"FALLBACK_FIRED keyframe_ref_missing kind=scene path={missing_scene}",
        f"FALLBACK_FIRED keyframe_ref_missing kind=pose path={missing_pose}",
        f"FALLBACK_FIRED keyframe_ref_missing kind=expression path={missing_expression}",
    ]
