#!/usr/bin/env python3
from __future__ import annotations

import json
import os
import sys
import tempfile
import unittest
from pathlib import Path
from unittest import mock


HERE = Path(__file__).resolve().parent
TEST_ROOT = Path(tempfile.mkdtemp(prefix="iterfact_iteration_modes_"))
(TEST_ROOT / "generated").mkdir(parents=True, exist_ok=True)
(TEST_ROOT / "analytics").mkdir(parents=True, exist_ok=True)

os.environ["ITERFACT_OUTPUT_DIR"] = str(TEST_ROOT / "generated")
os.environ["ITERFACT_ANALYTICS_DIR"] = str(TEST_ROOT / "analytics")
os.environ["ITERFACT_HEADLESS_VALIDATION_ENABLED"] = "0"
os.environ["ITERFACT_LIBRARY_PREVIEW_CAPTURE_ENABLED"] = "0"
os.environ["ITERFACT_PUBLIC_ARTIFACT_BASE_URL"] = "https://a.iterfact.test"
os.environ["ANTHROPIC_API_KEY"] = ""

if str(HERE) not in sys.path:
    sys.path.insert(0, str(HERE))

import server as mcp_server  # noqa: E402


ARTIFACT_ID = "artifact-iteration-modes"


def _write_state(feedback_notes: list[dict[str, object]] | None = None) -> None:
    state = {
        "artifact_id": ARTIFACT_ID,
        "created_at": "2026-03-22T12:00:00+00:00",
        "updated_at": "2026-03-24T12:00:00+00:00",
        "artifact_status": "published",
        "title": "Iteration Modes Artifact",
        "prompt": "Build a sharp investor-facing operating review.",
        "plan": {
            "meta": {"title": "Iteration Modes Artifact"},
            "wrapper": {"engine": "12"},
            "theme": "warm-editorial",
            "sections": [
                {"title": "Opening", "type": "narrative", "job": "Frame the thesis", "engine": "12"},
                {"title": "Evidence", "type": "evidence", "job": "Support the claims", "engine": "03"},
                {"title": "Close", "type": "cta", "job": "Land the recommendation", "engine": "12"},
            ],
        },
        "current": {
            "html_file": "20260324-120000-iteration-modes-artifact.html",
            "preview_url": "https://a.iterfact.test/generated/artifact-iteration-modes/20260324-120000-iteration-modes-artifact.html",
            "object_key": "generated/artifact-iteration-modes/20260324-120000-iteration-modes-artifact.html",
            "quality_score": 94,
            "warnings": [],
            "updated_at": "2026-03-24T12:00:00+00:00",
            "published_at": "2026-03-24T12:00:00+00:00",
            "generation_mode": "planned_render",
            "version_title": "Iteration Modes Artifact",
            "storage": "local",
        },
        "feedback_notes": feedback_notes or [],
    }
    state_path = mcp_server.artifact_state_path(ARTIFACT_ID)
    state_path.parent.mkdir(parents=True, exist_ok=True)
    state_path.write_text(json.dumps(state, ensure_ascii=True, indent=2), encoding="utf-8")


class IterationModeTests(unittest.IsolatedAsyncioTestCase):
    def setUp(self) -> None:
        _write_state(
            [
                {
                    "id": "note-1",
                    "status": "open",
                    "type": "copy",
                    "text": "Tighten the opening paragraph.",
                    "section_hint": "Opening",
                }
            ]
        )

    def test_build_artifact_sections_response_lists_saved_plan_sections(self) -> None:
        payload = mcp_server.build_artifact_sections_response(mcp_server.load_artifact_state(ARTIFACT_ID))

        self.assertEqual(payload["section_count"], 3)
        self.assertEqual(payload["sections"][0]["title"], "Opening")
        self.assertEqual(payload["sections"][1]["section_job"], "Support the claims")
        self.assertEqual(payload["sections"][2]["engine"], "12")

    async def test_prompt_only_feedback_revision_is_allowed(self) -> None:
        _write_state([])
        output_path = TEST_ROOT / "generated" / "prompt-only.html"
        output_path.write_text("<html></html>", encoding="utf-8")

        revised_plan = {
            "meta": {"title": "Iteration Modes Artifact"},
            "wrapper": {"engine": "12"},
            "theme": "warm-editorial",
            "sections": [
                {"title": "Opening", "type": "narrative", "job": "Frame the thesis", "engine": "12"},
                {"title": "Evidence", "type": "evidence", "job": "Support the claims", "engine": "03"},
                {"title": "Close", "type": "cta", "job": "Land the recommendation", "engine": "12"},
            ],
        }

        with (
            mock.patch.object(
                mcp_server,
                "revise_plan_from_feedback_with_anthropic",
                new=mock.AsyncMock(return_value=(revised_plan, {"_provider_events": []})),
            ) as revise_mock,
            mock.patch.object(
                mcp_server,
                "render_plan_with_iterative_refinement",
                new=mock.AsyncMock(
                    return_value=(
                        revised_plan,
                        "<html><body>Updated artifact</body></html>",
                        {},
                        [],
                        95,
                        {},
                    )
                ),
            ),
            mock.patch.object(
                mcp_server,
                "validate_delivery_ready_artifact",
                return_value=("<html><body>Updated artifact</body></html>", [], 95, {}),
            ),
            mock.patch.object(mcp_server, "allow_nonfatal_low_quality_publish", return_value=False),
            mock.patch.object(mcp_server, "enforce_render_validation_or_raise"),
            mock.patch.object(mcp_server, "public_quality_score", return_value=95),
            mock.patch.object(mcp_server, "critic_outcome_flags", return_value=(False, False)),
            mock.patch.object(
                mcp_server,
                "write_local_output",
                return_value=(ARTIFACT_ID, output_path),
            ),
            mock.patch.object(mcp_server, "persist_artifact_state"),
            mock.patch.object(mcp_server, "update_feedback_note_statuses") as update_feedback_mock,
        ):
            result = await mcp_server.apply_artifact_feedback_internal(
                artifact_id=ARTIFACT_ID,
                feedback_ids=[],
                additional_instruction="Make this read like a tighter partner memo.",
            )

        self.assertTrue(revise_mock.await_count == 1)
        self.assertEqual(revise_mock.await_args.kwargs["selected_feedback"], [])
        self.assertIn("prompt-only iteration update", result["notes"][0].lower())
        update_feedback_mock.assert_called_once_with(ARTIFACT_ID, [], status="applied")

    async def test_feedback_revision_still_requires_notes_or_prompt(self) -> None:
        _write_state([])

        with self.assertRaisesRegex(
            ValueError,
            "Add an iteration prompt or select at least one open feedback note",
        ):
            await mcp_server.apply_artifact_feedback_internal(
                artifact_id=ARTIFACT_ID,
                feedback_ids=[],
                additional_instruction=None,
            )


if __name__ == "__main__":
    unittest.main()
