"""Regression test for GitHub issue #211.

scripts/push_syllabus.py makes three requests calls (backup GET, PUT, verify
GET) on the authorized syllabus push path. Python's requests library has no
default timeout, so a hung connection on any of the three stalls the push
forever. Every call must set timeout=30 — the canonical value used by
canvas_api._request_with_retry and the rest of the repo.
"""

import os
import shutil
import sys
import unittest
from unittest.mock import MagicMock, patch

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

import push_syllabus

TEST_COURSE_ID = "88811"
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 _get_response(syllabus_body):
    resp = MagicMock()
    resp.raise_for_status = MagicMock()
    resp.json.return_value = {"syllabus_body": syllabus_body}
    return resp


class TestPushSyllabusTimeout(unittest.TestCase):
    """Every requests.get/requests.put call in push_syllabus() sets timeout=30."""

    def setUp(self):
        self.staging_dir = os.path.join(
            PLUGIN_ROOT, "courses", TEST_COURSE_ID, "staging", "syllabus")
        os.makedirs(self.staging_dir, exist_ok=True)
        self.staged_file = os.path.join(self.staging_dir, "syllabus_body.html")
        with open(self.staged_file, "w", encoding="utf-8") as f:
            f.write("<p>New syllabus content</p>")

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

    @patch("push_syllabus.lint_content", return_value=[])
    @patch("push_syllabus.canvas_api.get_config", return_value=_mock_config())
    def test_all_three_calls_set_timeout_30(self, mock_get_config, mock_lint):
        # First GET = pre-write backup fetch, second GET = post-write verify.
        backup_resp = _get_response("")
        verify_resp = _get_response("<p>New syllabus content</p>")
        mock_get = MagicMock(side_effect=[backup_resp, verify_resp])

        put_resp = MagicMock()
        put_resp.status_code = 200
        mock_put = MagicMock(return_value=put_resp)

        with patch("requests.get", mock_get), patch("requests.put", mock_put):
            result = push_syllabus.push_syllabus(TEST_COURSE_ID, dry_run=False)

        self.assertTrue(result, "push_syllabus() should succeed with this mock setup")
        self.assertEqual(mock_get.call_count, 2,
                         "expected one backup GET and one verify GET")
        self.assertEqual(mock_put.call_count, 1)

        for i, call in enumerate(mock_get.call_args_list):
            self.assertIn("timeout", call.kwargs,
                         f"requests.get call #{i + 1} missing timeout kwarg")
            self.assertEqual(call.kwargs["timeout"], 30,
                            f"requests.get call #{i + 1} timeout should be 30")

        put_call = mock_put.call_args_list[0]
        self.assertIn("timeout", put_call.kwargs,
                     "requests.put call missing timeout kwarg")
        self.assertEqual(put_call.kwargs["timeout"], 30,
                        "requests.put call timeout should be 30")


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