import time

from api.tests.helpers import (
	SchedulerTestCase,
	build_exam_world,
	create_admin_client,
	post_signed,
	signed_payload,
)

SCHEDULE_URL = "/api/admin/exam-activity/schedule"
SCHEDULE_DIRECT_URL = "/api/admin/exam-activity/schedule-direct"
UNSCHEDULE_URL = "/api/admin/exam-activity/unschedule"
INFO_URL = "/api/admin/exam-activity/info"
UPDATE_INVIGILATOR_URL = "/api/admin/session/update-invigilator"


class SchedulerAuthTests(SchedulerTestCase):
	def setUp(self):
		super().setUp()
		self.world = build_exam_world()
		self.session = self.world.add_session()
		self.protected = [
			(SCHEDULE_URL, {"id": [self.world.activity.id]}),
			(SCHEDULE_DIRECT_URL, {"id": [self.world.activity.id], "time_slot": self.world.monday_slot()}),
			(UNSCHEDULE_URL, {"id": [self.world.activity.id]}),
			(UPDATE_INVIGILATOR_URL, {"id": [self.session.id], "invigilator_id": []}),
		]

	def test_missing_timestamp_returns_400(self):
		for url, payload in self.protected:
			with self.subTest(url=url):
				signed = signed_payload(payload)
				signed.pop("timestamp")
				response = self.client.post(url, signed, format="json")
				self.assertEqual(response.status_code, 400)
				self.assertEqual(response.json()["code"], 400)

	def test_missing_signature_returns_400(self):
		for url, payload in self.protected:
			with self.subTest(url=url):
				body = dict(payload)
				body["timestamp"] = int(time.time())
				response = self.client.post(url, body, format="json")
				self.assertEqual(response.status_code, 400)
				self.assertEqual(response.json()["code"], 400)

	def test_invalid_signature_returns_400(self):
		for url, payload in self.protected:
			with self.subTest(url=url):
				signed = signed_payload(payload)
				signed["signature"] = "0" * 64
				response = self.client.post(url, signed, format="json")
				self.assertEqual(response.status_code, 400)
				self.assertEqual(response.json()["code"], 400)

	def test_expired_timestamp_returns_400(self):
		for url, payload in self.protected:
			with self.subTest(url=url):
				body = dict(payload)
				body["timestamp"] = int(time.time()) - 400
				response = self.client.post(url, signed_payload(body), format="json")
				self.assertEqual(response.status_code, 400)

	def test_missing_token_returns_403(self):
		from rest_framework.test import APIClient

		anonymous = APIClient()
		for url, payload in self.protected:
			with self.subTest(url=url):
				response = post_signed(anonymous, url, payload)
				self.assertEqual(response.status_code, 403)

	def test_missing_permission_returns_403(self):
		_user, client, _token = create_admin_client(permissions=[])
		for url, payload in self.protected:
			with self.subTest(url=url):
				response = post_signed(client, url, payload)
				self.assertEqual(response.status_code, 403)

	def test_invalid_activity_id_returns_400(self):
		for url in (SCHEDULE_URL, SCHEDULE_DIRECT_URL, UNSCHEDULE_URL):
			with self.subTest(url=url):
				payload = {"id": [999999999]}
				if url == SCHEDULE_DIRECT_URL:
					payload["time_slot"] = self.world.monday_slot()
				response = post_signed(self.client, url, payload)
				self.assertEqual(response.status_code, 400)
				self.assertEqual(response.json()["code"], 400)

	def test_missing_id_returns_400(self):
		for url in (SCHEDULE_URL, SCHEDULE_DIRECT_URL, UNSCHEDULE_URL, UPDATE_INVIGILATOR_URL):
			with self.subTest(url=url):
				payload = {}
				if url == SCHEDULE_DIRECT_URL:
					payload["time_slot"] = self.world.monday_slot()
				response = post_signed(self.client, url, payload)
				self.assertEqual(response.status_code, 400)

	def test_schedule_direct_missing_time_slot_returns_400(self):
		response = post_signed(self.client, SCHEDULE_DIRECT_URL, {"id": [self.world.activity.id]})
		self.assertEqual(response.status_code, 400)

	def test_info_requires_signature_but_not_permission(self):
		from rest_framework.test import APIClient

		anonymous = APIClient()
		payload = {
			"exam_activity_id": self.world.activity.id,
			"exam_period_id": self.world.period.id,
		}
		unsigned = {
			"exam_activity_id": self.world.activity.id,
			"exam_period_id": self.world.period.id,
			"timestamp": int(time.time()),
		}
		unsigned_response = anonymous.post(INFO_URL, unsigned, format="json")
		self.assertEqual(unsigned_response.status_code, 400)

		ok_response = post_signed(anonymous, INFO_URL, payload)
		self.assertEqual(ok_response.status_code, 200)
		self.assertEqual(ok_response.json()["code"], 200)

	def test_info_rejects_mismatched_period(self):
		other = build_exam_world()
		response = post_signed(
			self.client,
			INFO_URL,
			{
				"exam_activity_id": self.world.activity.id,
				"exam_period_id": other.period.id,
			},
		)
		self.assertEqual(response.status_code, 400)
		self.assertEqual(response.json()["code"], 400)
		self.assertIn("exam_period_id", response.json()["errors"])
