from __future__ import annotations

from typing import Iterable
import re

from schools.cbe import JUNIOR_SUBJECT_SPECS, LOWER_PRIMARY_SUBJECT_SPECS, UPPER_PRIMARY_SUBJECT_SPECS
from schools.models import ClassRoom, EducationLevel, Subject as SchoolSubject
from academics.models import Subject as AcademicSubject


CBE_LEVELS_BY_CATEGORY = {
    "PRIMARY": ["Pre School", "Kindergarten", "Lower Primary", "Upper Primary"],
    "JUNIOR": ["Junior"],
    "SENIOR": ["Senior"],
    "COMPREHENSIVE": ["Pre School", "Kindergarten", "Lower Primary", "Upper Primary", "Junior"],
    "ALL_THROUGH": ["Pre School", "Kindergarten", "Lower Primary", "Upper Primary", "Junior", "Senior"],
}

CAMBRIDGE_LEVELS_BY_CATEGORY = {
    "PRIMARY": ["Cambridge Primary"],
    "JUNIOR": ["Cambridge Lower Secondary"],
    "SENIOR": ["Cambridge Upper Secondary (IGCSE)", "Cambridge Advanced (AS & A Level)"],
    "COMPREHENSIVE": ["Cambridge Primary", "Cambridge Lower Secondary"],
    "ALL_THROUGH": ["Kindergarten", "Cambridge Primary", "Cambridge Lower Secondary", "Cambridge Upper Secondary (IGCSE)", "Cambridge Advanced (AS & A Level)"],
}

CBE_CLASSES_BY_LEVEL = {
    "Pre School": ["Pre School", "Pre-Primary 1 (PP1)", "Pre-Primary 2 (PP2)"],
    "Kindergarten": ["Kindergarten"],
    "Lower Primary": ["Grade 1", "Grade 2", "Grade 3"],
    "Upper Primary": ["Grade 4", "Grade 5", "Grade 6"],
    "Junior": ["Grade 7", "Grade 8", "Grade 9"],
    "Senior": ["Grade 10", "Grade 11", "Grade 12"],
}

CAMBRIDGE_CLASSES_BY_LEVEL = {
    "Kindergarten": ["Baby Class", "Middle Class", "Reception"],
    "Cambridge Primary": ["Year 1", "Year 2", "Year 3", "Year 4", "Year 5", "Year 6"],
    "Cambridge Lower Secondary": ["Year 7", "Year 8", "Year 9"],
    "Cambridge Upper Secondary (IGCSE)": ["Year 10", "Year 11"],
    "Cambridge Advanced (AS & A Level)": ["Year 12", "Year 13"],
}

CAMBRIDGE_PRIMARY_SUBJECTS = [
    "Art & Design",
    "Computing",
    "Digital Literacy",
    "English",
    "English as a Second Language",
    "Global Perspectives",
    "Humanities",
    "Mathematics",
    "Modern Foreign Language",
    "Music",
    "Physical Education",
    "Science",
    "Wellbeing",
]

CAMBRIDGE_LOWER_SECONDARY_SUBJECTS = [
    "Art & Design",
    "Computing",
    "Digital Literacy",
    "English",
    "English as a Second Language",
    "Global Perspectives",
    "Humanities",
    "Mathematics",
    "Modern Foreign Language",
    "Music",
    "Physical Education",
    "Science",
    "Wellbeing",
]

CAMBRIDGE_IGCSE_SUBJECTS = [
    "Accounting",
    "Accounting (9-1)",
    "Afrikaans - Second Language",
    "Agriculture",
    "Arabic - First Language",
    "Arabic - First Language (9-1)",
    "Arabic - Foreign Language",
    "Arabic (9-1)",
    "Art & Design",
    "Art & Design (9-1)",
    "Bahasa Indonesia",
    "Biology",
    "Biology (9-1)",
    "Business Studies",
    "Business Studies (9-1)",
    "Chemistry",
    "Chemistry (9-1)",
    "Chinese - First Language",
    "Chinese - Second Language",
    "Chinese (Mandarin) - Foreign Language",
    "Chinese (Mandarin) (9-1)",
    "Classical Greek",
    "Combined Science",
    "Computer Science",
    "Computer Science (9-1)",
    "Co-ordinated Sciences (Double)",
    "Co-ordinated Sciences (9-1)",
    "Design & Technology",
    "Design & Technology (9-1)",
    "Drama",
    "Drama (9-1)",
    "Economics",
    "Economics (9-1)",
    "English",
    "English (as an Additional Language)",
    "English as a Second Language",
    "English as a Second Language (9-1)",
    "English - First Language",
    "English - First Language (9-1)",
    "English Literature",
    "English Literature (9-1)",
    "Enterprise",
    "Environmental Management",
    "Food & Nutrition",
    "French - Foreign Language",
    "French - Foreign Language (9-1)",
    "Geography",
    "Geography (9-1)",
    "German - Foreign Language",
    "German - Foreign Language (9-1)",
    "Global Perspectives",
    "Greek - Foreign Language",
    "Hindi as a Second Language",
    "History",
    "History (9-1)",
    "Information and Communication Technology",
    "Islamic Studies",
    "Islamiyat",
    "Italian - Foreign Language",
    "Japanese - Foreign Language",
    "Latin",
    "Literature (English)",
    "Malay - First Language",
    "Malay - Foreign Language",
    "Marine Science",
    "Mathematics",
    "Mathematics - Additional",
    "Mathematics - International",
    "Mathematics (9-1)",
    "Mathematics (US)",
    "Music",
    "Music (9-1)",
    "Pakistan Studies",
    "Physical Education",
    "Physical Education (9-1)",
    "Physical Science",
    "Physics",
    "Physics (9-1)",
    "Portuguese - First Language",
    "Psychology",
    "Religious Studies",
    "Sanskrit",
    "Science - Combined",
    "Setswana - First Language",
    "Sociology",
    "Spanish - First Language",
    "Spanish - Foreign Language",
    "Spanish - Literature in Spanish",
    "Spanish (9-1)",
    "Statistics",
    "Swahili",
    "Thai - First Language",
    "Travel & Tourism",
    "Turkish - First Language",
    "Urdu as a Second Language",
    "Vietnamese - First Language",
    "World Literature",
]

CAMBRIDGE_AS_A_SUBJECTS = [
    "Accounting",
    "Afrikaans - Language (AS Level only)",
    "Arabic",
    "Arabic - Language (AS Level only)",
    "Art & Design",
    "Biblical Studies",
    "Biology",
    "Business",
    "Chemistry",
    "Chinese - Language & Literature (A Level only)",
    "Chinese Language (AS Level only)",
    "Classical Studies",
    "Computer Science",
    "Design & Technology",
    "Digital Media & Design",
    "Drama",
    "Economics",
    "English - Language",
    "English General Paper (AS Level only)",
    "English Language & Literature",
    "English Literature",
    "Environmental Management",
    "Food Studies (AS Level only)",
    "French",
    "Geography",
    "German",
    "Global Perspectives & Research",
    "Hindi",
    "History",
    "Information Technology",
    "Islamic Studies",
    "Japanese Language (AS Level only)",
    "Law",
    "Literature in English",
    "Marine Science (AS Level only)",
    "Mathematics",
    "Mathematics - Further",
    "Media Studies",
    "Music",
    "Nepal Studies (AS Level only)",
    "Physical Education",
    "Physics",
    "Portuguese",
    "Psychology",
    "Sociology",
    "Spanish",
    "Thinking Skills (AS Level only)",
    "Travel & Tourism",
    "Urdu - Language (AS Level only)",
]


def _ensure_levels(level_names: Iterable[str]) -> dict[str, EducationLevel]:
    level_map: dict[str, EducationLevel] = {}
    for name in level_names:
        level_obj, _ = EducationLevel.objects.get_or_create(name=name)
        level_map[name] = level_obj
    return level_map


def _seed_classes_for_school(school, level_map: dict[str, EducationLevel], class_map: dict[str, list[str]]) -> None:
    order = 1
    for level_name, class_names in class_map.items():
        if level_name not in level_map:
            continue
        level_obj = level_map[level_name]
        for class_name in class_names:
            ClassRoom.objects.get_or_create(
                school=school,
                name=class_name,
                defaults={"level": level_obj, "order": order},
            )
            order += 1


def _seed_cbe_subjects_for_school(school, level_map: dict[str, EducationLevel]) -> None:
    if not school or getattr(school, "system_type", "") != "CBE":
        return

    def ensure_subjects(specs, level_name):
        level_obj = level_map.get(level_name)
        if not level_obj:
            return
        for spec in specs:
            name = spec["name"]
            code = spec["code"]
            short = spec["short_name"]
            SchoolSubject.objects.get_or_create(
                school=school,
                name=name,
                education_level=level_obj,
                defaults={"code": code, "short_name": short},
            )
            AcademicSubject.objects.get_or_create(
                school=school,
                name=name,
                education_level=level_obj,
                defaults={"short_name": short},
            )

    ensure_subjects(LOWER_PRIMARY_SUBJECT_SPECS, "Lower Primary")
    ensure_subjects(UPPER_PRIMARY_SUBJECT_SPECS, "Upper Primary")
    ensure_subjects(JUNIOR_SUBJECT_SPECS, "Junior")


def seed_defaults_for_new_school(school) -> None:
    category = getattr(school, "school_category", "") or "PRIMARY"
    school_type = getattr(school, "school_type", "")
    if school_type == "CAMBRIDGE":
        level_names = CAMBRIDGE_LEVELS_BY_CATEGORY.get(category, CAMBRIDGE_LEVELS_BY_CATEGORY["PRIMARY"])
        level_map = _ensure_levels(level_names)
        class_map = {k: v for k, v in CAMBRIDGE_CLASSES_BY_LEVEL.items() if k in level_names}
        _seed_classes_for_school(school, level_map, class_map)
        _seed_cambridge_subjects_for_school(school, level_map)
        return

    level_names = CBE_LEVELS_BY_CATEGORY.get(category, CBE_LEVELS_BY_CATEGORY["PRIMARY"])
    level_map = _ensure_levels(level_names)
    class_map = {k: v for k, v in CBE_CLASSES_BY_LEVEL.items() if k in level_names}
    _seed_classes_for_school(school, level_map, class_map)
    _seed_cbe_subjects_for_school(school, level_map)
    try:
        from schools.views import _seed_missing_primary_comments
        _seed_missing_primary_comments(school)
    except Exception:
        pass


def _build_subject_code(base: str) -> str:
    cleaned = re.sub(r"[^A-Za-z0-9]+", "", base.upper())
    return cleaned[:8] or "SUBJ"


def _fit_short_name(model, value: str) -> str:
    max_length = model._meta.get_field("short_name").max_length
    return value[:max_length] if max_length else value


def _unique_subject_code(school, level_obj: EducationLevel, name: str) -> str:
    base = _build_subject_code(name)
    code = base
    suffix = 1
    while SchoolSubject.objects.filter(
        school=school,
        education_level=level_obj,
        code=code,
    ).exists():
        suffix += 1
        code = f"{base[:6]}{suffix}"
    return code


def _ensure_subject(school, level_obj: EducationLevel, name: str) -> None:
    if SchoolSubject.objects.filter(school=school, name__iexact=name, education_level=level_obj).exists():
        return
    code = _unique_subject_code(school, level_obj, name)
    school_short = _fit_short_name(SchoolSubject, name)
    academic_short = _fit_short_name(AcademicSubject, name)
    SchoolSubject.objects.get_or_create(
        school=school,
        name=name,
        education_level=level_obj,
        defaults={"code": code, "short_name": school_short},
    )
    AcademicSubject.objects.get_or_create(
        school=school,
        name=name,
        education_level=level_obj,
        defaults={"short_name": academic_short},
    )

def _seed_cambridge_subjects_for_school(school, level_map: dict[str, EducationLevel]) -> None:
    primary = level_map.get("Cambridge Primary")
    lower = level_map.get("Cambridge Lower Secondary")
    upper = level_map.get("Cambridge Upper Secondary (IGCSE)")
    advanced = level_map.get("Cambridge Advanced (AS & A Level)")

    if primary:
        for name in CAMBRIDGE_PRIMARY_SUBJECTS:
            _ensure_subject(school, primary, name)
    if lower:
        for name in CAMBRIDGE_LOWER_SECONDARY_SUBJECTS:
            _ensure_subject(school, lower, name)
    if upper:
        for name in CAMBRIDGE_IGCSE_SUBJECTS:
            _ensure_subject(school, upper, name)
    if advanced:
        for name in CAMBRIDGE_AS_A_SUBJECTS:
            _ensure_subject(school, advanced, name)
