from __future__ import annotations

import json
from pathlib import Path

import pytest

from recoil.pipeline.orchestrator import ingest_pipeline as ip


@pytest.fixture()
def pipeline(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> ip.IngestPipeline:
    # REC-164: enrichment now re-stamps the project-level bible record after the
    # write, which resolves ProjectPaths.for_project(project). Point the projects
    # root at a tmp dir so the conftest shim auto-creates the project and the
    # stamp stays hermetic (the manifest write failing FAILs the stage by design).
    projects_root = tmp_path / "projects"
    projects_root.mkdir()
    monkeypatch.setenv("RECOIL_PROJECTS_ROOT", str(projects_root))

    pipe = object.__new__(ip.IngestPipeline)
    pipe.project = "demo"
    pipe.project_root = tmp_path
    pipe.bible_path = tmp_path / "global_bible.json"
    (tmp_path / "bible").mkdir()
    return pipe


def _write_bible(path: Path) -> None:
    path.write_text(
        json.dumps(
            {
                "project": "demo",
                "characters": {
                    "SADIE": {
                        "display_name": "Sadie",
                        "visual_description": "[OPUS_ENRICHMENT]",
                    }
                },
                "locations": {},
                "props": {},
            }
        ),
        encoding="utf-8",
    )


def test_call_opus_routes_through_oauth_and_preserves_logging_keys(
    pipeline: ip.IngestPipeline,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    calls: list[tuple[str, str, str]] = []

    def fake_call_opus(model_id: str, system_prompt: str, user_prompt: str) -> str:
        calls.append((model_id, system_prompt, user_prompt))
        return "  FIELD: characters.SADIE.visual_description\nVALUE: precise face description  "

    monkeypatch.setattr(ip, "call_opus_oauth", fake_call_opus)

    result = pipeline._call_opus("system", "user")

    assert calls == [(ip.OPUS_ENRICHMENT_MODEL, "system", "user")]
    assert result == {
        "text": "FIELD: characters.SADIE.visual_description\nVALUE: precise face description",
        "cost": 0.0,
        "input_tokens": 0,
        "output_tokens": 0,
    }


def test_run_opus_enrichment_runs_without_anthropic_api_key(
    pipeline: ip.IngestPipeline,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
    _write_bible(pipeline.bible_path)
    calls: list[tuple[str, str, str]] = []

    def fake_call_opus(model_id: str, system_prompt: str, user_prompt: str) -> str:
        calls.append((model_id, system_prompt, user_prompt))
        return (
            "FIELD: characters.SADIE.visual_description\n"
            "VALUE: late 20s, lean angular build, warm olive skin, dark eyes"
        )

    monkeypatch.setattr(ip, "call_opus_oauth", fake_call_opus)

    result = pipeline.run_opus_enrichment(pipeline.bible_path)
    saved = json.loads(pipeline.bible_path.read_text(encoding="utf-8"))

    assert calls
    assert "reason" not in result
    assert result["filled"] == 1
    assert result["failed"] == []
    assert result["details"] == [
        {
            "entity": "characters.SADIE",
            "fields_requested": 1,
            "fields_filled": 1,
            "cost": 0.0,
        }
    ]
    assert saved["characters"]["SADIE"]["visual_description"] == (
        "late 20s, lean angular build, warm olive skin, dark eyes"
    )


def test_run_opus_enrichment_oauth_error_degrades_field(
    pipeline: ip.IngestPipeline,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
    _write_bible(pipeline.bible_path)

    def fake_call_opus(*_args, **_kwargs) -> str:
        raise ip.OpusOAuthError(
            "claude OAuth call failed",
            model_id=ip.OPUS_ENRICHMENT_MODEL,
            returncode=1,
            stderr="boom",
        )

    monkeypatch.setattr(ip, "call_opus_oauth", fake_call_opus)

    result = pipeline.run_opus_enrichment(pipeline.bible_path)
    saved = json.loads(pipeline.bible_path.read_text(encoding="utf-8"))

    assert result["filled"] == 0
    assert result["failed"] == ["characters.SADIE.visual_description"]
    assert result["details"][0]["entity"] == "characters.SADIE"
    assert result["details"][0]["fields_filled"] == 0
    assert "claude OAuth call failed" in result["details"][0]["error"]
    assert saved["characters"]["SADIE"]["visual_description"].startswith(
        "[OPUS_ENRICHMENT_FAILED:"
    )
