"""Phase 1 safety-floor tests for staging schema, feedback gates, and fetch adapter."""

import json
import os
import shutil
import sys
import unittest
from pathlib import Path
from unittest.mock import patch

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts"))

import canvas_api
import fetch_canvas_to_stage
import preflight_checks
import push_to_canvas
import staging_manager
import staging_schema


TEST_COURSE_ID = "99994"
PLUGIN_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


def _mock_config():
    return {
        "token": "fake",
        "domain": "canvas.test.edu",
        "course_id": TEST_COURSE_ID,
        "base_url": "https://canvas.test.edu/api/v1",
        "course_url": f"https://canvas.test.edu/api/v1/courses/{TEST_COURSE_ID}",
        "headers": {"Authorization": "Bearer fake"},
    }


def _valid_quiz_payload(**question_overrides):
    question = {
        "id": 101,
        "question_name": "Q1",
        "question_text": "<p>Choose one.</p>",
        "question_type": "multiple_choice_question",
        "points_possible": 1,
        "correct_comments": "Correct because...",
        "incorrect_comments": "Incorrect because...",
        "answers": [
            {"answer_text": "A", "answer_weight": 100},
            {"answer_text": "B", "answer_weight": 0},
        ],
    }
    question.update(question_overrides)
    return {"quiz": {"title": "Safety Quiz"}, "questions": [question]}


class SafetyFloorTest(unittest.TestCase):
    def tearDown(self):
        course_dir = os.path.join(PLUGIN_ROOT, "courses", TEST_COURSE_ID)
        if os.path.exists(course_dir):
            shutil.rmtree(course_dir)

    def test_staging_schema_rejects_unknown_quiz_question_type(self):
        payload = _valid_quiz_payload(question_type="hotspot_question")

        result = staging_manager.stage(
            TEST_COURSE_ID,
            "quizzes",
            "123",
            payload,
            validate=False,
            title="Bad Quiz",
            source_canvas_id="123",
        )

        self.assertFalse(result["ok"])
        codes = {err["code"] for err in result["schema"]["errors"]}
        self.assertIn("unknown_question_type", codes)

    def test_staging_schema_accepts_matching_numerical_fill_and_ordering_shapes(self):
        payload = {
            "quiz": {"title": "Mixed Quiz"},
            "questions": [
                {
                    "id": 1,
                    "question_name": "Matching",
                    "question_text": "Match.",
                    "question_type": "matching_question",
                    "points_possible": 1,
                    "answers": [{"answer_match_left": "Left", "answer_match_right": "Right"}],
                },
                {
                    "id": 2,
                    "question_name": "Numerical",
                    "question_text": "Number.",
                    "question_type": "numerical_question",
                    "points_possible": 1,
                    "answers": [{"numerical_answer_type": "exact_answer", "exact": 4.0}],
                },
                {
                    "id": 3,
                    "question_name": "Blank",
                    "question_text": "Fill [x].",
                    "question_type": "fill_in_multiple_blanks_question",
                    "points_possible": 1,
                    "answers": [{"blank_id": "x", "answer_text": "value"}],
                },
                {
                    "id": 4,
                    "question_name": "Ordering",
                    "question_text": "Order.",
                    "question_type": "ordering_question",
                    "points_possible": 1,
                    "answers": [{"answer_text": "First", "answer_order": 1}],
                },
            ],
        }

        result = staging_manager.stage(
            TEST_COURSE_ID,
            "quizzes",
            "456",
            payload,
            validate=False,
            title="Mixed Quiz",
            source_canvas_id="456",
            destructive_reconcile_ok=True,
        )

        self.assertTrue(result["ok"])
        schema = staging_schema.validate(TEST_COURSE_ID, "quizzes", "456")
        self.assertTrue(schema["ok"])

    def test_identical_feedback_preflight_warning_and_push_gate(self):
        payload = _valid_quiz_payload(
            correct_comments="Same feedback",
            incorrect_comments="Same feedback",
        )
        staging_manager.stage(
            TEST_COURSE_ID,
            "quizzes",
            "789",
            payload,
            validate=True,
            title="Feedback Quiz",
            source_canvas_id="789",
        )

        issues = preflight_checks.check_quiz_tier1(payload)
        self.assertTrue(any(issue.criterion_id == "identical_feedback" for issue in issues))

        blocked = push_to_canvas.push_quiz(_mock_config(), "789", TEST_COURSE_ID, dry_run=True)
        self.assertFalse(blocked["ok"])
        self.assertIn("identical_feedback", blocked)

        allowed = push_to_canvas.push_quiz(
            _mock_config(),
            "789",
            TEST_COURSE_ID,
            dry_run=True,
            allow_identical_feedback=True,
        )
        self.assertTrue(allowed["ok"])
        self.assertIn("identical_feedback", allowed)

    def test_fetch_quiz_preserves_question_ids_and_normalizes_answer_read_shape(self):
        with patch("fetch_canvas_to_stage.canvas_api.get_quiz", return_value={
            "id": 2020276,
            "title": "M1 Mockup Quiz",
            "quiz_type": "assignment",
            "allowed_attempts": -1,
        }), patch("fetch_canvas_to_stage.canvas_api.get_quiz_questions", return_value=[
            {
                "id": 9001,
                "question_name": "Q1",
                "question_type": "multiple_choice_question",
                "points_possible": 2.0,
                "question_text": "<p>Question?</p>",
                "answers": [
                    {
                        "id": 10,
                        "text": "<p>Answer A</p>",
                        "weight": 100.0,
                        "comments": "Good",
                        "comments_html": "<p>Good</p>",
                    }
                ],
            }
        ]):
            staged = fetch_canvas_to_stage.fetch_quiz(_mock_config(), "2020276")

        self.assertEqual(staged["quiz"]["canvas_quiz_id"], 2020276)
        self.assertEqual(staged["questions"][0]["id"], 9001)
        self.assertEqual(staged["questions"][0]["points_possible"], 2)
        answer = staged["questions"][0]["answers"][0]
        self.assertEqual(answer["id"], 10)
        self.assertEqual(answer["answer_text"], "<p>Answer A</p>")
        self.assertEqual(answer["answer_weight"], 100)
        self.assertEqual(answer["answer_comments"], "Good")
        self.assertEqual(answer["answer_comment_html"], "<p>Good</p>")

    def test_fetch_cli_can_write_tmp_payload_without_staging(self):
        out = Path("/tmp/asucb-p1-fetch-test.json")
        try:
            with patch("fetch_canvas_to_stage.canvas_api.get_config", return_value=_mock_config()), \
                 patch("fetch_canvas_to_stage.canvas_api.get_quiz", return_value={
                     "id": 2020276,
                     "title": "M1 Mockup Quiz",
                     "quiz_type": "assignment",
                     "allowed_attempts": -1,
                 }), \
                 patch("fetch_canvas_to_stage.canvas_api.get_quiz_questions", return_value=[
                     {
                         "id": 9001,
                         "question_name": "Q1",
                         "question_type": "short_answer_question",
                         "points_possible": 1,
                         "question_text": "Answer.",
                         "answers": [{"text": "Yes", "weight": 100.0}],
                     }
                 ]):
                rc = fetch_canvas_to_stage.main([
                    "--course-id", TEST_COURSE_ID,
                    "--type", "quiz",
                    "--id", "2020276",
                    "--output", str(out),
                    "--no-stage",
                ])
            self.assertEqual(rc, 0)
            data = json.loads(out.read_text(encoding="utf-8"))
            self.assertEqual(data["questions"][0]["id"], 9001)
        finally:
            try:
                out.unlink()
            except FileNotFoundError:
                pass


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