import re

from django import forms

from .phone_numbers import PhoneNumberField
from .models import School

BOOLEAN_FIELDS = {
    "availabilityOfSanitaryPadForEmergency",
    "easyAccessForDisabledStudents",
    "electricity",
    "handWashingArea",
    "hasCcvtUsedInSchool",
    "hasChildClub",
    "hasClassroomForEveryClass",
    "hasComputer",
    "hasLibrary",
    "hasMedicalFacility",
    "hasMidDayMeal",
    "hasSmartDigitalBoard",
    "internet",
    "isPta",
    "playgroundStatus",
    "schoolBus",
    "scienceLab",
    "testOfDrinkingWater",
    "toiletForGirls",
}


def _label_for_key(key):
    return key.replace("_", " ").replace("class_", "Class ").replace("organization", "Organization").replace("municipality", "Municipality").replace("district", "District").replace("headTeacher", "Head teacher").replace("contact", "Contact").replace("total_students", "Total students")


def _group_for_key(key):
    lowered = key.lower()
    if any(word in lowered for word in ["district", "municipality", "province", "ward", "address"]):
        return "Location"
    if any(word in lowered for word in ["contact", "email", "phone", "headteacher", "teacher"]):
        return "Contact"
    if any(word in lowered for word in ["class_", "student", "total_students", "registered", "teacher"]):
        return "Students"
    if any(word in lowered for word in ["library", "electricity", "internet", "computer", "lab", "playground", "toilet", "water", "sanitary", "midday", "bus", "medical", "board", "club"]):
        return "Facilities"
    if any(word in lowered for word in ["organization", "type", "date", "approved", "completion", "auditor", "tenure"]):
        return "School details"
    return "Other"


def _parse_boolean(value, key=None):
    if isinstance(value, bool):
        return value
    if isinstance(value, (int, float)) and not isinstance(value, bool):
        if key == "toiletForGirls":
            return value > 0
        return value == 1
    if isinstance(value, str):
        normalized = value.strip().lower()
        if normalized in {"true", "1", "yes", "y", "on"}:
            return True
        if normalized in {"false", "0", "no", "n", "off"}:
            return False
    return None


def _looks_boolean(value, key):
    if key in BOOLEAN_FIELDS or isinstance(value, bool):
        return True
    return isinstance(value, str) and value.strip().lower() in {
        "true", "false", "yes", "no", "y", "n", "on", "off"
    }


class SchoolForm(forms.ModelForm):
    class Meta:
        model = School
        fields = [
            "organization_id",
            "name",
            "district_id",
            "district_name",
            "municipality",
            "school_type",
            "total_students",
            "up_to_class",
        ]
        widgets = {
            "name": forms.TextInput(attrs={"placeholder": "School name"}),
            "district_name": forms.TextInput(attrs={"placeholder": "District name"}),
            "municipality": forms.TextInput(attrs={"placeholder": "Municipality"}),
            "school_type": forms.TextInput(attrs={"placeholder": "School type"}),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        is_existing = bool(self.instance and self.instance.pk)
        stored_data = self.instance.data if is_existing else {}
        template_data = School.objects.values_list("data", flat=True).first() or {}
        field_data = {**template_data, **stored_data}
        keys = sorted(field_data)
        groups = {"Location": [], "Contact": [], "Students": [], "Facilities": [], "School details": [], "Other": []}
        self.extra_fields = []
        for key in keys:
            type_hint = field_data.get(key)
            value = stored_data.get(key) if is_existing else None
            field_name = f"extra_{re.sub(r'[^a-zA-Z0-9_]+', '_', key).strip('_')}_{len(self.extra_fields)}"
            if "phone" in key.lower() or "contactnumber" in key.lower():
                field = PhoneNumberField(
                    required=False,
                    initial="" if value is None else str(value),
                    label=_label_for_key(key),
                    widget=forms.TextInput(attrs={"type": "tel", "inputmode": "tel"}),
                )
            elif _looks_boolean(type_hint, key):
                field = forms.TypedChoiceField(
                    choices=(("", "Not recorded"), ("true", "Yes"), ("false", "No")),
                    coerce=lambda selected: None if selected == "" else selected == "true",
                    empty_value=None,
                    initial="" if not is_existing or value is None else str(_parse_boolean(value, key)).lower(),
                    required=False,
                    label=_label_for_key(key),
                )
            elif isinstance(type_hint, (int, float)) and not isinstance(type_hint, bool):
                field = forms.IntegerField(required=False, initial=value if is_existing else None, label=_label_for_key(key))
            else:
                field = forms.CharField(required=False, initial="" if value is None else str(value), label=_label_for_key(key),
                                        widget=forms.TextInput(attrs={"placeholder": key}))
            self.fields[field_name] = field
            group = _group_for_key(key)
            groups.setdefault(group, []).append({"label": _label_for_key(key), "name": field_name})
            self.extra_fields.append({"key": key, "label": _label_for_key(key), "name": field_name, "group": group})
        self.field_groups = [
            {"title": title, "fields": fields}
            for title, fields in groups.items()
            if fields
        ]

    def save(self, commit=True):
        school = super().save(commit=False)
        merged = dict(school.data or {})
        for item in getattr(self, "extra_fields", []):
            key = item["key"]
            name = item["name"]
            value = self.cleaned_data.get(name)
            if value is None:
                merged[key] = None
            elif value == "":
                merged.pop(key, None)
            else:
                merged[key] = value
        school.data = merged
        if commit:
            school.save()
        return school
