from datetime import datetime

from rest_framework import status
from rest_framework.exceptions import ValidationError

from api.models.audit_trail import AuditTrail
from api.models.audit_trail_details import AuditTrailDetails
from api.models.es_exam_activity import EsExamActivity
from api.models.es_exam_requirement import EsExamRequirement
from api.translation import __
from api.utils import log_critical_error, get_exception_detail, get_add_new_data, get_ip
from api.validator import BaseValidator
from api.views.admin.base import AdminApiBase


class ExamRequirementCreate(AdminApiBase):
	def parse_datetime_value(self, value, field):
		if value in (None, ""):
			return None

		if isinstance(value, datetime):
			return value.replace(tzinfo=None)

		datetime_formats = (
			"%Y-%m-%d %H:%M",
			"%Y-%m-%d %H:%M:%S",
			"%Y-%m-%dT%H:%M",
			"%Y-%m-%dT%H:%M:%S",
		)
		for dt_format in datetime_formats:
			try:
				return datetime.strptime(str(value), dt_format)
			except (ValueError, TypeError):
				pass

		message = __("validation.invalid", field=__(f"attr.{field}"))
		raise ValidationError({
			"error": message,
			"errors": {
				field: [message],
			},
		})

	def validate_request(self, request):
		rules = {
			"exam_period_id": "required|exists:api.EsExamPeriod,id",
			"code": "required|not_exists:api.EsExamRequirement,code",
			"name": "required",
			"description": "nullable",
			"planned_size": "required|integer|gte:0",
			"writing_time": "required|time",
			"fixed_start_date": "nullable|date",
			"reading_time": "required|time",
			"fixed_start_time": "nullable|time",
			"location_required": "nullable",
			"exclusive_use": "nullable",
			"minimum_split_size": "nullable|integer|gte:0",
			"earliest_start": "nullable",
			"latest_end": "nullable",
				"invigilator_suitability": "nullable|array|exists:api.EsSuitability,id",
				"location_suitability": "nullable|array|exists:api.EsSuitability,id",
		}

		# if need rename field, can put here
		attribute = {
			"description": __("attr.desc"),
		}

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

		earliest_start = self.parse_datetime_value(request.data.get("earliest_start"), "earliest_start")
		latest_end = self.parse_datetime_value(request.data.get("latest_end"), "latest_end")

		if earliest_start and latest_end and earliest_start > latest_end:
			message = __("validation.gt.date", field=__("attr.latest_end"), value=request.data.get("earliest_start"))
			raise ValidationError({
				"error": message,
				"errors": {
					"latest_end": message,
				},
			})

	def post(self, request):
		# Validate input
		try:
			self.validate_request(request)

			old_new_data = {
				"new_data": None,
			}

			action = "exam_requirement_create"  # this is for audit log only

			idata = {}
			valid_fields = {f.column for f in EsExamRequirement._meta.fields}

			for key, val in request.data.items():
				if key in valid_fields:
					idata[key] = val

			idata["created_by"] = request.user.id
			exam_requirement = EsExamRequirement.objects.create(**idata)

			old_new_data["new_data"] = get_add_new_data(exam_requirement)

			invigilator_suitability_ids = request.data.get("invigilator_suitability", [])
			location_suitability_ids = request.data.get("location_suitability", [])

			if invigilator_suitability_ids:
				self.update_m2m_field(exam_requirement, "invigilator_suitability", None, invigilator_suitability_ids, old_new_data)

			if location_suitability_ids:
				self.update_m2m_field(exam_requirement, "location_suitability", None, location_suitability_ids, old_new_data)

			# Auto-create the first exam activity for this requirement
			exam_activity = EsExamActivity.objects.create(
				exam_requirement=exam_requirement,
				code=f"{exam_requirement.code}-01",
				name=exam_requirement.name,
				is_scheduled=False,
				created_by=request.user.id,
			)

			if invigilator_suitability_ids:
				exam_activity.invigilator_suitability.set(invigilator_suitability_ids)
			if location_suitability_ids:
				exam_activity.location_suitability.set(location_suitability_ids)

			# insert audit trail and details
			# can insert the parent first, then only insert details
			audit_trail = AuditTrail.objects.create(
				user_id=request.user.id,
				type=self.audit_type,
				ip_address=get_ip(request),
			)
			remark_param = {
				"code": exam_requirement.code,
			}

			# call to a function for insert details, so in future if want change to used worker, can modify in function only
			AuditTrailDetails.custom_insert(
				audit_trail=audit_trail,
				action=action,
				remark_param=remark_param,
				new_data=old_new_data["new_data"],
			)
			response = {

			}

			return self.api_response(data=response)
		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)
