import copy
import json

from api.models import EsSetting

SETTING_PARAM = "invigilator_setting"

DEFAULT_INVIGILATOR_SETTING = {
	"fixed_invigilators": [{"quantity": 0, "role_ids": []}],
	"students_count": 0,
	"invigilators_count": 0,
	"include_fixed_invigilators": False,
}


def parse_bool(value, default=False):
	if value in (None, ""):
		return default
	if isinstance(value, bool):
		return value
	if isinstance(value, (int, float)) and value in (0, 1):
		return bool(value)
	if isinstance(value, str):
		normalized = value.strip().lower()
		if normalized in ("1", "true", "yes", "on"):
			return True
		if normalized in ("0", "false", "no", "off"):
			return False
	return default


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


def normalize_invigilator_setting(raw=None):
	data = raw if isinstance(raw, dict) else {}
	rows = data.get("fixed_invigilators")
	if not isinstance(rows, list):
		rows = []

	fixed_invigilators = []
	for row in rows:
		if not isinstance(row, dict):
			continue
		role_ids = row.get("role_ids") or []
		if not isinstance(role_ids, list):
			role_ids = []
		normalized_role_ids = []
		for role_id in role_ids:
			parsed = _as_int(role_id, default=None)
			if parsed is None:
				continue
			if parsed not in normalized_role_ids:
				normalized_role_ids.append(parsed)
		fixed_invigilators.append({
			"quantity": max(_as_int(row.get("quantity"), 0), 0),
			"role_ids": normalized_role_ids,
		})

	return {
		"fixed_invigilators": fixed_invigilators,
		"students_count": max(_as_int(data.get("students_count"), 0), 0),
		"invigilators_count": max(_as_int(data.get("invigilators_count"), 0), 0),
		"include_fixed_invigilators": parse_bool(data.get("include_fixed_invigilators"), False),
	}


def load_invigilator_setting():
	row = EsSetting.objects.filter(param=SETTING_PARAM).first()
	if not row or not row.value:
		return copy.deepcopy(DEFAULT_INVIGILATOR_SETTING)

	try:
		parsed = json.loads(row.value)
	except (TypeError, ValueError, json.JSONDecodeError):
		return copy.deepcopy(DEFAULT_INVIGILATOR_SETTING)

	return normalize_invigilator_setting(parsed)


def save_invigilator_setting(data):
	normalized = normalize_invigilator_setting(data)
	EsSetting.objects.update_or_create(
		param=SETTING_PARAM,
		defaults={
			"value": json.dumps(normalized),
			"comment": "Fixed invigilators, student-to-invigilator ratio, and include/exclude fixed staff",
		},
	)
	return normalized


def compute_invigilator_counts(students_enrolled, setting):
	setting = normalize_invigilator_setting(setting)
	students_enrolled = max(_as_int(students_enrolled, 0), 0)
	students_count = setting["students_count"]
	invigilators_count = setting["invigilators_count"]
	fixed_count = sum(row["quantity"] for row in setting["fixed_invigilators"])

	if students_count <= 0 or invigilators_count <= 0:
		ratio_required = 0
	else:
		ratio_required = (
			students_enrolled * invigilators_count + students_count - 1
		) // students_count

	if setting["include_fixed_invigilators"]:
		auto_count = max(0, ratio_required - fixed_count)
		total_required = max(ratio_required, fixed_count)
	else:
		auto_count = ratio_required
		total_required = ratio_required + fixed_count

	return {
		"ratio_required": ratio_required,
		"fixed_count": fixed_count,
		"auto_count": auto_count,
		"total_required": total_required,
	}
