from datetime import timedelta

from rest_framework import status
from rest_framework.exceptions import ValidationError

from api.models import EsExamActivity, EsExamRequirementStudent
from api.translation import __
from api.utils import get_exception_detail, log_critical_error
from api.validator import BaseValidator
from api.views.admin.base import AdminApiBase
from api.views.admin.exam_activity_list import ExamActivityList
from api.services.exam_scheduler import available_session_starts_for_activity


class ExamActivityInfo(AdminApiBase):
	def validate_request(self, request):
		rules = {
			"exam_activity_id": "required|integer|exists:api.EsExamActivity,id",
			"exam_period_id": "required|integer|exists:api.EsExamPeriod,id",
		}
		attribute = {
			"exam_activity_id": __("attr.activity_id"),
			"exam_period_id": __("attr.exam_period_id"),
		}

		validator = BaseValidator(request.data, rules, attribute)
		error = validator.validate()
		if error:
			raise ValidationError(error)

	def serialize_info(self, activity):
		requirement = activity.exam_requirement
		duration = ExamActivityList.build_duration(requirement.writing_time, requirement.reading_time)
		duration_slots = ExamActivityList.build_duration_slots(requirement.writing_time, requirement.reading_time)
		scheduled_start_at = ExamActivityList.slot_to_datetime(activity.time_slot)
		scheduled_end_at = scheduled_start_at + timedelta(minutes=duration_slots * 30) if scheduled_start_at else None

		return {
			"id": activity.id,
			"location_id": activity.location_id,
			"location": activity.location.name if activity.location else None,
			"code": activity.code,
			"name": activity.name,
			"time_slot": activity.time_slot,
			"is_scheduled": activity.is_scheduled,
			"invigilator_suitability": [
				{"id": suitability.id, "name": suitability.name}
				for suitability in activity.invigilator_suitability.all()
			],
			"location_suitability": [
				{"id": suitability.id, "name": suitability.name}
				for suitability in activity.location_suitability.all()
			],
			"duration": duration,
			"scheduled_start_at": scheduled_start_at,
			"scheduled_end_at": scheduled_end_at,
			"exam_requirement_id": activity.exam_requirement_id,
			"exam_requirement_code": requirement.code,
			"exam_requirement_name": requirement.name,
			"exam_requirements": requirement.name,
			"exam_period_id": requirement.exam_period_id,
			"exam_period": requirement.exam_period.name if requirement.exam_period else None,
			"planned_size": requirement.planned_size,
			"writing_time": requirement.writing_time,
			"fixed_start_date": requirement.fixed_start_date,
			"reading_time": requirement.reading_time,
			"fixed_start_time": requirement.fixed_start_time,
			"location_required": requirement.location_required,
			"exclusive_use": requirement.exclusive_use,
			"minimum_split_size": requirement.minimum_split_size,
			"earliest_start": requirement.earliest_start,
			"latest_end": requirement.latest_end,
			"real_size": EsExamRequirementStudent.objects.filter(
				exam_requirement_id=activity.exam_requirement_id,
				student_id__isnull=False,
			).count(),
		}

	def post(self, request):
		try:
			self.api_log_skip_outgoing_data = True
			self.validate_request(request)

			exam_activity_id = int(request.data.get("exam_activity_id"))
			exam_period_id = int(request.data.get("exam_period_id"))

			activity = (
				EsExamActivity.objects
				.select_related("exam_requirement", "exam_requirement__exam_period", "location")
				.prefetch_related("invigilator_suitability", "location_suitability")
				.filter(id=exam_activity_id)
				.first()
			)

			if activity.exam_requirement.exam_period_id != exam_period_id:
				message = __("validation.invalid", field=__("attr.exam_period_id"))
				raise ValidationError({
					"error": message,
					"errors": {
						"exam_period_id": [message],
					},
				})

			return self.api_response(data={
				"info": self.serialize_info(activity),
				"session_start": available_session_starts_for_activity(activity),
			})
		except ValidationError as e:
			first_message = e.detail["error"]
			errors = e.detail["errors"]
			return self.api_response(error=first_message, errors=errors, code=status.HTTP_400_BAD_REQUEST)
		except Exception as e:
			e_details = get_exception_detail(e)
			log_critical_error(user_id=None, descr=e_details["descr"], url=e_details["url"], trace=e_details["trace"])
			return self.api_response(error=__("message.internal_server_error"), code=status.HTTP_500_INTERNAL_SERVER_ERROR)
