import json

from flask import Flask

from abstra_internals.server.routes.mcp import get_editor_bp
from tests.fixtures import BaseTest

DELETE_STAGE_TOOL = "delete_stage__req_approval__"
MISSING_STAGE_ID = "stage-that-does-not-exist"


class DeleteStageMCPTest(BaseTest):
    def setUp(self):
        super().setUp()
        self.app = Flask(__name__)
        self.app.register_blueprint(get_editor_bp(self.controller), url_prefix="/mcp")
        self.client = self.app.test_client()
        self.headers = {
            "Content-Type": "application/json",
            "MCP-Protocol-Version": "2025-06-18",
        }

    def _call_delete_stage(self, stage_id: str, remove_file: bool = False):
        return self.client.post(
            "/mcp/",
            json={
                "jsonrpc": "2.0",
                "id": 1,
                "method": "tools/call",
                "params": {
                    "name": DELETE_STAGE_TOOL,
                    "arguments": {"stage_id": stage_id, "remove_file": remove_file},
                },
            },
            headers=self.headers,
        )

    @staticmethod
    def _result_text(response) -> str:
        return response.get_json()["result"]["content"][0]["text"]

    def _create_form(self, title: str, file: str):
        return self.controller.create_stage("form", title, file)

    def test_deleting_missing_stage_differs_from_successful_delete(self):
        stage = self._create_form("Doomed Form", "doomed.py")

        success = self._call_delete_stage(stage.id)
        missing = self._call_delete_stage(MISSING_STAGE_ID)

        self.assertNotEqual(
            success.get_data(as_text=True),
            missing.get_data(as_text=True),
        )

    def test_successful_delete_is_not_reported_as_not_found(self):
        stage = self._create_form("Doomed Form", "doomed.py")

        response = self._call_delete_stage(stage.id)

        self.assertEqual(response.status_code, 200)
        self.assertNotIn("not found", self._result_text(response))

    def test_successful_delete_reports_status_and_stage_id(self):
        stage = self._create_form("Doomed Form", "doomed.py")

        payload = json.loads(self._result_text(self._call_delete_stage(stage.id)))

        self.assertEqual(payload["status"], "success")
        self.assertEqual(payload["stage_id"], stage.id)
        self.assertFalse(payload["file_removed"])
        self.assertIsNone(self.controller.get_stage(stage.id))

    def test_successful_delete_with_remove_file_reports_removed_file(self):
        stage = self._create_form("Doomed Form", "doomed.py")
        path = self.root / "doomed.py"
        self.assertTrue(path.exists())

        payload = json.loads(
            self._result_text(self._call_delete_stage(stage.id, remove_file=True))
        )

        self.assertTrue(payload["file_removed"])
        self.assertFalse(path.exists())

    def test_deleting_missing_stage_returns_json_rpc_error(self):
        response = self._call_delete_stage(MISSING_STAGE_ID)

        self.assertEqual(response.status_code, 400)
        message = response.get_json()["error"]["message"]
        self.assertIn(MISSING_STAGE_ID, message)

    def test_deleting_missing_stage_raises_value_error(self):
        with self.assertRaises(ValueError) as context:
            self.controller.delete_stage(MISSING_STAGE_ID)

        self.assertIn(MISSING_STAGE_ID, str(context.exception))

    def test_delete_stage_if_exists_reports_missing_without_raising(self):
        result = self.controller.delete_stage_if_exists(MISSING_STAGE_ID)

        self.assertEqual(result["status"], "not_found")
        self.assertFalse(result["file_removed"])

    def test_rest_delete_stays_idempotent(self):
        stage = self._create_form("Doomed Form", "doomed.py")
        editor = self.get_editor_flask_client()

        first = editor.delete(f"/_editor/api/forms/{stage.id}")
        second = editor.delete(f"/_editor/api/forms/{stage.id}")
        missing = editor.delete(f"/_editor/api/forms/{MISSING_STAGE_ID}")

        self.assertEqual(first.status_code, 200)
        self.assertEqual(second.status_code, 200)
        self.assertEqual(missing.status_code, 200)
