from django.db import transaction

from api.models import EsInvigilatorSetting, EsInvigilatorSettingRole

DEFAULT_INVIGILATOR_SETTING = {
	"configuration": [],
}


def _as_int(value, default=0):
	try:
		if isinstance(value, bool):
			return default
		return int(value)
	except (TypeError, ValueError):
		return default


def _unique_role_ids(role_ids):
	if not isinstance(role_ids, list):
		return []
	normalized = []
	for role_id in role_ids:
		parsed = _as_int(role_id, default=None)
		if parsed is None or parsed == EsInvigilatorSetting.ANY_ROLE_ID or parsed in normalized:
			continue
		normalized.append(parsed)
	return normalized


def serialize_invigilator_setting_row(row, role_ids=None):
	if role_ids is None:
		role_ids = [role.id for role in row.role.all()]
	role_ids = list(role_ids)
	if not role_ids:
		role_ids = [EsInvigilatorSetting.ANY_ROLE_ID]
	return {
		"id": row.id,
		"role_ids": role_ids,
		"is_floating": row.is_floating,
		"is_inclusive": row.is_inclusive,
		"quantity_mode": row.quantity_mode,
		"quantity": row.quantity,
		"students_count": row.students_count,
		"priority": row.priority,
	}


def load_invigilator_setting():
	rows = list(EsInvigilatorSetting.objects.prefetch_related("role").order_by("-priority", "id"))
	return {
		"configuration": [serialize_invigilator_setting_row(row) for row in rows],
	}


def save_invigilator_setting(data, user_id=None):
	payload = data if isinstance(data, dict) else {}
	rows = payload.get("configuration")
	if not isinstance(rows, list):
		rows = []

	with transaction.atomic():
		keep_ids = []
		for index, row in enumerate(rows):
			if not isinstance(row, dict):
				continue
			quantity_mode = _as_int(row.get("quantity_mode"), 0)
			students_count = row.get("students_count")
			if quantity_mode == EsInvigilatorSetting.QUANTITY_MODE_TO_CODE["ratio"]:
				students_count = max(_as_int(students_count, 0), 0)
			else:
				students_count = None
			fields = {
				"is_floating": 1 if _as_int(row.get("is_floating"), 0) == 1 else 0,
				"is_inclusive": 1 if _as_int(row.get("is_inclusive"), 0) == 1 else 0,
				"quantity_mode": quantity_mode,
				"quantity": max(_as_int(row.get("quantity"), 0), 0),
				"students_count": students_count,
				"priority": _as_int(row.get("priority"), 0),
				"sort_order": index,
				"updated_by": user_id,
			}
			setting_id = _as_int(row.get("id"), default=None)
			setting = None
			if setting_id is not None:
				setting = EsInvigilatorSetting.objects.filter(id=setting_id).first()
			if setting is None:
				setting = EsInvigilatorSetting.objects.create(
					**fields,
					created_by=user_id,
				)
			else:
				for key, value in fields.items():
					setattr(setting, key, value)
				setting.save(update_fields=[*fields.keys(), "updated_at"])

			keep_ids.append(setting.id)
			role_ids = _unique_role_ids(row.get("role_ids"))
			EsInvigilatorSettingRole.objects.filter(setting=setting).delete()
			if role_ids:
				EsInvigilatorSettingRole.objects.bulk_create([
					EsInvigilatorSettingRole(setting=setting, role_id=role_id)
					for role_id in role_ids
				])

		delete_qs = EsInvigilatorSetting.objects.all()
		if keep_ids:
			delete_qs = delete_qs.exclude(id__in=keep_ids)
		delete_qs.delete()

	return load_invigilator_setting()


def compute_rule_quantity(rule, students):
	quantity = max(_as_int(rule.get("quantity") if isinstance(rule, dict) else None, 0), 0)
	if quantity <= 0:
		return 0
	mode = _as_int(rule.get("quantity_mode") if isinstance(rule, dict) else None, 0)
	if mode == EsInvigilatorSetting.QUANTITY_MODE_TO_CODE["fixed"]:
		return quantity
	students_count = max(_as_int(rule.get("students_count") if isinstance(rule, dict) else None, 0), 0)
	students = max(_as_int(students, 0), 0)
	if students_count <= 0:
		return 0
	return (students * quantity + students_count - 1) // students_count
