from collections import defaultdict
from decimal import Decimal, InvalidOperation
from io import BytesIO
from django.contrib import messages
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User, Group
from django.db import transaction
from django.db.models import Sum
from django.http import FileResponse, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone

from .forms import (
    SchoolForm, ParticipantForm, ItemForm, PointRuleForm,
    AcademicYearForm, UserCreateForm, UserPasswordForm,
)
from .models import (
    School, Participant, ParticipantItem, Item, GroupEntry, GroupMember,
    PointRule, Result, SchoolPoint, ParticipantPoint, AcademicYear,
    SystemSetting, StageMark,
)
from .services import recompute_results, publish_item, reset_item

def group_exists(user, name):
    return user.groups.filter(name=name).exists()

def is_admin(user):
    return user.is_authenticated and user.is_superuser

def is_school_user(user):
    return user.is_authenticated and group_exists(user, "School User")

def is_result_user(user):
    return user.is_authenticated and group_exists(user, "Result Entry User")

def current_school(user):
    if is_school_user(user) and not is_admin(user):
        return School.objects.filter(code=user.username).first()
    return None

def require_admin(view):
    return user_passes_test(is_admin, login_url="/login/")(view)

def can_manage_school(user, school):
    return is_admin(user) or (is_school_user(user) and current_school(user) and current_school(user).id == school.id)

def school_locked(school):
    return bool(school and school.confirmed)

# ---------------- Authentication / Home ----------------
def home(request):
    return render(request, "home.html", {
        "school_count": School.objects.count(),
        "participant_count": Participant.objects.count(),
        "item_count": Item.objects.filter(active=True).count(),
        "published_count": Result.objects.filter(published=True).count(),
    })

def login_view(request):
    if request.user.is_authenticated:
        return redirect("home")
    if request.method == "POST":
        user = authenticate(
            request,
            username=request.POST.get("username", "").strip(),
            password=request.POST.get("password", ""),
        )
        if user:
            login(request, user)
            return redirect("home")
        messages.error(request, "Invalid username or password.")
    return render(request, "login.html")

def logout_view(request):
    logout(request)
    return redirect("home")

@login_required
def change_password(request):
    if request.method == "POST":
        old = request.POST.get("old_password", "")
        new = request.POST.get("new_password", "")
        confirm = request.POST.get("confirm_password", "")
        if not request.user.check_password(old):
            messages.error(request, "Current password is incorrect.")
        elif len(new) < 6:
            messages.error(request, "New password must contain at least 6 characters.")
        elif new != confirm:
            messages.error(request, "New passwords do not match.")
        else:
            request.user.set_password(new)
            request.user.save()
            messages.success(request, "Password changed. Please log in again.")
            logout(request)
            return redirect("login")
    return render(request, "change_password.html")

@login_required
def dashboard(request):
    return render(request, "dashboard.html")

# ---------------- School Management ----------------
@require_admin
def schools(request):
    if request.method == "POST":
        form = SchoolForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "School saved successfully.")
            return redirect("schools")
    else:
        form = SchoolForm()
    return render(request, "admin/schools.html", {
        "schools": School.objects.all(),
        "form": form,
    })

@require_admin
def school_edit(request, pk):
    school = get_object_or_404(School, pk=pk)
    form = SchoolForm(request.POST or None, instance=school)
    if form.is_valid():
        form.save()
        messages.success(request, "School updated successfully.")
        return redirect("schools")
    return render(request, "admin/form.html", {"form": form, "title": "Edit School"})

@require_admin
def school_delete(request, pk):
    school = get_object_or_404(School, pk=pk)
    if request.method == "POST":
        school.delete()
        messages.success(request, "School deleted.")
    return redirect("schools")

# ---------------- School Profile ----------------
@login_required
def profile(request):
    school = current_school(request.user)
    if not school:
        return redirect("home")
    form = SchoolForm(request.POST or None, instance=school)
    form.fields["name"].disabled = True
    form.fields["code"].disabled = True
    if form.is_valid() and not school_locked(school):
        form.save()
        messages.success(request, "Profile updated.")
        return redirect("profile")
    return render(request, "school/profile.html", {"form": form, "school": school})

# ---------------- Participants ----------------
@login_required
def participants(request):
    school = current_school(request.user)
    selected_id = request.GET.get("school")
    if is_admin(request.user):
        selected = School.objects.filter(pk=selected_id).first() if selected_id else None
    else:
        selected = school
    qs = Participant.objects.select_related("school")
    if selected:
        qs = qs.filter(school=selected)
    section_order = {"UP": 1, "HS": 2, "HSS": 3, "LP": 4}
    participants = list(qs)
    participants.sort(key=lambda p: (section_order.get(p.section, 9), p.sex, p.name.lower()))
    return render(request, "participants/list.html", {
        "participants": participants,
        "schools": School.objects.all(),
        "selected_school": selected,
        "locked": school_locked(selected) if selected else False,
    })

@login_required
def participant_add(request):
    if is_admin(request.user):
        school = School.objects.filter(pk=request.GET.get("school") or request.POST.get("school")).first()
    else:
        school = current_school(request.user)
    if not school:
        return redirect("participants")
    if school_locked(school) and not is_admin(request.user):
        messages.error(request, "School data is confirmed. Admin must reset the confirmation.")
        return redirect("participants")
    form = ParticipantForm(request.POST or None)
    if form.is_valid():
        p = form.save(commit=False)
        p.school = school
        p.save()
        messages.success(request, "Participant added.")
        return redirect(f"/participants/?school={school.id}" if is_admin(request.user) else "participants")
    return render(request, "form.html", {"form": form, "title": "Add Participant", "school": school})

@login_required
def participant_edit(request, pk):
    p = get_object_or_404(Participant.objects.select_related("school"), pk=pk)
    if not can_manage_school(request.user, p.school):
        return redirect("participants")
    if school_locked(p.school) and not is_admin(request.user):
        messages.error(request, "School data is confirmed.")
        return redirect("participants")
    form = ParticipantForm(request.POST or None, instance=p)
    if form.is_valid():
        form.save()
        messages.success(request, "Participant updated.")
        return redirect("participants")
    return render(request, "form.html", {"form": form, "title": "Edit Participant", "school": p.school})

@login_required
def participant_delete(request, pk):
    p = get_object_or_404(Participant.objects.select_related("school"), pk=pk)
    if request.method == "POST" and can_manage_school(request.user, p.school):
        if not school_locked(p.school) or is_admin(request.user):
            p.delete()
            messages.success(request, "Participant deleted.")
    return redirect("participants")

# ---------------- Participant Items ----------------
@login_required
def participant_items(request):
    school = current_school(request.user)
    selected = School.objects.filter(pk=request.GET.get("school")).first() if is_admin(request.user) else school
    qs = Participant.objects.select_related("school").prefetch_related("item_links__item")
    if selected:
        qs = qs.filter(school=selected)
    return render(request, "participants/items.html", {
        "participants": qs,
        "schools": School.objects.all(),
        "selected_school": selected,
    })

@login_required
def participant_item_edit(request, pk):
    p = get_object_or_404(Participant.objects.select_related("school"), pk=pk)
    if not can_manage_school(request.user, p.school):
        return redirect("participant_items")
    if school_locked(p.school) and not is_admin(request.user):
        messages.error(request, "School data is confirmed.")
        return redirect("participant_items")
    items = Item.objects.filter(active=True, section=p.section).order_by("code")
    selected = set(p.item_links.values_list("item_id", flat=True))
    if request.method == "POST":
        chosen = request.POST.getlist("items")
        if len(chosen) > 5:
            messages.error(request, "Maximum 5 items are allowed per participant.")
        else:
            p.item_links.all().delete()
            for item_id in chosen:
                item = get_object_or_404(items, pk=item_id)
                p.item_links.create(item=item)
            messages.success(request, "Participant items updated.")
            return redirect("participant_items")
    return render(request, "participants/item_edit.html", {
        "participant": p, "items": items, "selected": selected
    })

# ---------------- Group Captains ----------------
@login_required
def captains(request):
    school = current_school(request.user)
    selected = School.objects.filter(pk=request.GET.get("school")).first() if is_admin(request.user) else school
    group_items = Item.objects.filter(active=True, participation_type="G").order_by("section", "code")
    qs = GroupEntry.objects.select_related("school", "item", "captain").prefetch_related("members__participant")
    if selected:
        qs = qs.filter(school=selected)
    if request.method == "POST" and selected and (is_admin(request.user) or not school_locked(selected)):
        item = get_object_or_404(group_items, pk=request.POST.get("item_id"))
        captain = get_object_or_404(Participant, pk=request.POST.get("captain_id"), school=selected)
        entry, _ = GroupEntry.objects.get_or_create(school=selected, item=item, defaults={"captain": captain})
        if entry.captain_id != captain.id:
            entry.captain = captain
            entry.save(update_fields=["captain"])
        messages.success(request, "Group captain saved.")
        return redirect("captains")
    captains = list(qs)
    return render(request, "participants/captains.html", {
        "captains": captains,
        "group_items": group_items,
        "participants": Participant.objects.filter(school=selected) if selected else Participant.objects.none(),
        "schools": School.objects.all(),
        "selected_school": selected,
    })

@login_required
def group_members(request, entry_id):
    entry = get_object_or_404(GroupEntry.objects.select_related("school", "item"), pk=entry_id)
    if not can_manage_school(request.user, entry.school):
        return redirect("captains")
    if school_locked(entry.school) and not is_admin(request.user):
        messages.error(request, "School data is confirmed.")
        return redirect("captains")
    members = list(entry.members.values_list("participant_id", flat=True))
    if request.method == "POST":
        selected = [int(x) for x in request.POST.getlist("members") if x.isdigit()]
        if len(selected) < entry.item.min_participants or len(selected) > entry.item.max_participants:
            messages.error(request, f"Participants must be between {entry.item.min_participants} and {entry.item.max_participants}.")
        else:
            entry.members.all().delete()
            for pid in selected:
                entry.members.create(participant_id=pid)
            messages.success(request, "Group members saved.")
            return redirect("captains")
    return render(request, "participants/group_members.html", {
        "entry": entry,
        "participants": Participant.objects.filter(school=entry.school).order_by("section", "sex", "name"),
        "members": set(members),
    })

# ---------------- Confirmation ----------------
@login_required
def confirmation(request):
    school = current_school(request.user)
    if not school:
        return redirect("home")
    if request.method == "POST" and not school.confirmed:
        password = request.POST.get("password", "")
        if request.user.check_password(password):
            school.confirmed = True
            school.confirmed_at = timezone.now()
            school.save(update_fields=["confirmed", "confirmed_at"])
            messages.success(request, "School data confirmed and locked.")
        else:
            messages.error(request, "Incorrect password.")
    return render(request, "school/confirmation.html", {"school": school})

@require_admin
def confirmation_status(request):
    if request.method == "POST":
        action = request.POST.get("action")
        school = get_object_or_404(School, pk=request.POST.get("school_id"))
        if action == "reset":
            school.confirmed = False
            school.confirmed_at = None
            school.save(update_fields=["confirmed", "confirmed_at"])
        elif action == "confirm":
            school.confirmed = True
            school.confirmed_at = timezone.now()
            school.save(update_fields=["confirmed", "confirmed_at"])
        messages.success(request, f"Confirmation updated for {school.name}.")
    return render(request, "admin/confirmation_status.html", {"schools": School.objects.all()})

# ---------------- Admin settings ----------------
@require_admin
def point_distribution(request):
    items = Item.objects.filter(active=True).order_by("section", "code")
    if request.method == "POST":
        for item in items:
            rule, _ = PointRule.objects.get_or_create(
                item=item,
                defaults={"first": 10 if item.participation_type == "G" else 5,
                          "second": 6 if item.participation_type == "G" else 3,
                          "third": 3 if item.participation_type == "G" else 1},
            )
            rule.first = int(request.POST.get(f"first_{item.id}", rule.first))
            rule.second = int(request.POST.get(f"second_{item.id}", rule.second))
            rule.third = int(request.POST.get(f"third_{item.id}", rule.third))
            rule.save()
        messages.success(request, "Point distribution updated.")
        return redirect("point_distribution")
    for item in items:
        PointRule.objects.get_or_create(
            item=item,
            defaults={"first": 10 if item.participation_type == "G" else 5,
                      "second": 6 if item.participation_type == "G" else 3,
                      "third": 3 if item.participation_type == "G" else 1},
        )
    return render(request, "admin/points.html", {"items": items})

@require_admin
def maximum_participation(request):
    items = Item.objects.filter(participation_type="G", active=True).order_by("section", "code")
    if request.method == "POST":
        item = get_object_or_404(Item, pk=request.POST.get("item_id"))
        item.min_participants = max(1, int(request.POST.get("min_participants", item.min_participants)))
        item.max_participants = max(item.min_participants, int(request.POST.get("max_participants", item.max_participants)))
        item.save()
        messages.success(request, "Maximum participation updated.")
        return redirect("maximum_participation")
    settings = {
        "individual_per_student": SystemSetting.get_int("individual_per_student", 5),
        "group_per_student": SystemSetting.get_int("group_per_student", 3),
    }
    return render(request, "admin/maximum.html", {"items": items, "settings": settings})

@require_admin
def save_general_limits(request):
    if request.method == "POST":
        for key in ["individual_per_student", "group_per_student"]:
            value = request.POST.get(key)
            if value and value.isdigit():
                SystemSetting.objects.update_or_create(key=key, defaults={"value": value})
        messages.success(request, "Participation limits saved.")
    return redirect("maximum_participation")

@require_admin
def academic_year(request):
    form = AcademicYearForm(request.POST or None)
    if form.is_valid():
        form.save()
        messages.success(request, "Academic year saved.")
        return redirect("academic_year")
    return render(request, "admin/academic_year.html", {"form": form, "years": AcademicYear.objects.all()})

# ---------------- Items (admin) ----------------
@require_admin
def items(request):
    form = ItemForm(request.POST or None)
    if form.is_valid():
        item = form.save()
        PointRule.objects.get_or_create(
            item=item,
            defaults={"first": 10 if item.participation_type == "G" else 5,
                      "second": 6 if item.participation_type == "G" else 3,
                      "third": 3 if item.participation_type == "G" else 1},
        )
        messages.success(request, "Item saved.")
        return redirect("items")
    return render(request, "admin/items.html", {"items": Item.objects.all(), "form": form})

@require_admin
def item_edit(request, pk):
    item = get_object_or_404(Item, pk=pk)
    form = ItemForm(request.POST or None, instance=item)
    if form.is_valid():
        form.save()
        messages.success(request, "Item updated.")
        return redirect("items")
    return render(request, "admin/form.html", {"form": form, "title": "Edit Item"})

@require_admin
def item_delete(request, pk):
    item = get_object_or_404(Item, pk=pk)
    if request.method == "POST":
        item.delete()
        messages.success(request, "Item deleted.")
    return redirect("items")

# ---------------- Chest numbers ----------------
@require_admin
def distribute_chest(request):
    if request.method == "POST":
        if School.objects.filter(confirmed=False).exists():
            messages.error(request, "All schools must be confirmed before chest-number distribution.")
            return redirect("distribute_chest")
        last = Participant.objects.exclude(chest_no__isnull=True).order_by("-chest_no").values_list("chest_no", flat=True).first() or 99
        changed = 0
        for p in Participant.objects.filter(chest_no__isnull=True).order_by("school__code", "section", "sex", "name"):
            last += 1
            p.chest_no = last
            p.save(update_fields=["chest_no"])
            changed += 1
        messages.success(request, f"Chest numbers assigned to {changed} new participants.")
    return render(request, "admin/distribute_chest.html")


@login_required
def report_confirmation(request):
    if is_admin(request.user):
        schools = School.objects.all()
    elif is_school_user(request.user):
        school = current_school(request.user)
        schools = School.objects.filter(pk=school.pk) if school else School.objects.none()
    else:
        schools = School.objects.all()
    return render(request, "admin/confirmation_status.html", {"schools": schools})

# ---------------- User Management ----------------
@require_admin
def user_management(request):
    if request.method == "POST":
        action = request.POST.get("action")
        if action == "create":
            form = UserCreateForm(request.POST)
            if form.is_valid():
                data = form.cleaned_data
                user = User.objects.create_user(
                    username=data["username"],
                    password=data["password"],
                )
                role = data["role"]
                if role == "admin":
                    user.is_staff = True
                    user.is_superuser = True
                    user.save()
                else:
                    group_name = "School User" if role == "school" else "Result Entry User"
                    group, _ = Group.objects.get_or_create(name=group_name)
                    user.groups.add(group)
                messages.success(request, f"{data['username']} created.")
                return redirect("user_management")
        elif action == "password":
            form = UserPasswordForm(request.POST)
            if form.is_valid():
                target = form.cleaned_data["user"]
                target.set_password(form.cleaned_data["password"])
                target.save()
                messages.success(request, f"Password changed for {target.username}.")
                return redirect("user_management")
        elif action == "delete":
            target = get_object_or_404(User, pk=request.POST.get("user_id"))
            if target.id != request.user.id:
                target.delete()
                messages.success(request, "User deleted.")
            else:
                messages.error(request, "You cannot delete your own account.")
            return redirect("user_management")
    else:
        form = UserCreateForm()
    return render(request, "admin/user_management.html", {
        "form": form,
        "users": User.objects.prefetch_related("groups").order_by("username"),
    })

# ---------------- Public / authenticated Results ----------------
def _result_queryset(request, published_only=True):
    qs = Result.objects.select_related("item", "participant__school", "group_entry__school")
    if published_only:
        qs = qs.filter(published=True)
    school_id = request.GET.get("school")
    section = request.GET.get("section")
    item_id = request.GET.get("item")
    participant_id = request.GET.get("participant")
    if school_id:
        qs = qs.filter(
            participant__school_id=school_id
        ) if participant_id or section or item_id else qs.filter(
            participant__school_id=school_id
        )
        qs = qs | Result.objects.filter(group_entry__school_id=school_id, published=published_only).select_related("item","participant__school","group_entry__school") if published_only else qs | Result.objects.filter(group_entry__school_id=school_id)
    if section:
        qs = qs.filter(item__section=section)
    if item_id:
        qs = qs.filter(item_id=item_id)
    if participant_id:
        qs = qs.filter(participant_id=participant_id)
    return qs.distinct().order_by("item__section", "item__code", "position", "id")

def full_result(request):
    results = _result_queryset(request, published_only=True)
    return render(request, "results/full_result.html", {
        "results": results,
        "schools": School.objects.all(),
        "items": Item.objects.filter(active=True),
        "participants": Participant.objects.all(),
    })

def published_results(request):
    items = Item.objects.filter(active=True).order_by("section", "code")
    status = {i.id: i.results.filter(published=True).exists() for i in items}
    return render(request, "results/published.html", {"items": items, "status": status})

@login_required
def school_championship(request):
    rows = []
    for school in School.objects.all():
        total = school.point_rows.filter(result__published=True).aggregate(v=Sum("points"))["v"] or 0
        sections = {}
        for sec in ["HSS", "HS", "UP", "LP"]:
            sections[sec] = school.point_rows.filter(result__published=True, result__item__section=sec).aggregate(v=Sum("points"))["v"] or 0
        rows.append({"school": school, "total": total, **sections})
    order = request.GET.get("order", "-total")
    rows.sort(key=lambda x: x["total"], reverse=(order == "-total"))
    return render(request, "results/school_championship.html", {"rows": rows})

@login_required
def championship(request):
    people = Participant.objects.annotate(total_points=Sum("point_rows__points")).order_by("-total_points", "name")
    champions = {}
    for sec in ["LP", "UP", "HS", "HSS"]:
        champions[sec] = {
            "M": list(people.filter(section=sec, sex="M")[:3]),
            "F": list(people.filter(section=sec, sex="F")[:3]),
        }
    kalaprathibha = people.filter(sex="M", item_links__item__participation_type="I").distinct().first()
    kalathilakam = people.filter(sex="F", item_links__item__participation_type="I").distinct().first()
    return render(request, "results/championship.html", {
        "champions": champions,
        "kalaprathibha": kalaprathibha,
        "kalathilakam": kalathilakam,
    })

# ---------------- Result Entry ----------------
@login_required
def result_entry(request):
    if not (is_admin(request.user) or is_result_user(request.user)):
        return redirect("home")
    code = request.GET.get("item_code", "").strip()
    item = Item.objects.filter(code=code, active=True).first() if code else None
    results = []
    if item:
        if item.participation_type == "I":
            competitors = Participant.objects.filter(
                item_links__item=item
            ).distinct().order_by("chest_no", "name")
            for p in competitors:
                r, _ = Result.objects.get_or_create(item=item, participant=p)
                results.append(r)
        else:
            groups = GroupEntry.objects.filter(
                item=item
            ).select_related("school", "captain").prefetch_related("members")
            for entry in groups:
                r, _ = Result.objects.get_or_create(item=item, group_entry=entry)
                results.append(r)
        results = list(Result.objects.filter(item=item).select_related("participant__school", "group_entry__school").order_by("position", "id"))

    if request.method == "POST" and item:
        if Result.objects.filter(item=item, confirmed=True).exists() and request.POST.get("action") in {"save", "confirm"}:
            messages.error(request, "This result is confirmed. Use Result Reset first.")
            return redirect(f"/results/entry/?item_code={item.code}")
        for r in results:
            pid = r.participant_id
            prefix = f"row_{r.id}_"
            try:
                r.mark1 = Decimal(request.POST.get(prefix+"m1", "0") or "0")
                r.mark2 = Decimal(request.POST.get(prefix+"m2", "0") or "0")
                r.mark3 = Decimal(request.POST.get(prefix+"m3", "0") or "0")
            except InvalidOperation:
                r.mark1 = r.mark2 = r.mark3 = Decimal("0")
            r.total = r.mark1 + r.mark2 + r.mark3
            r.save()
        recompute_results(item)
        if request.POST.get("action") == "confirm":
            Result.objects.filter(item=item).update(confirmed=True)
            messages.success(request, "Result confirmed.")
        else:
            messages.success(request, "Result saved.")
        return redirect(f"/results/entry/?item_code={item.code}")

    return render(request, "results/entry.html", {
        "item": item,
        "results": results,
        "code": code,
    })

@login_required
def publish_result(request):
    if not (is_admin(request.user) or is_result_user(request.user)):
        return redirect("home")
    if request.method == "POST":
        item = get_object_or_404(Item, pk=request.POST.get("item_id"))
        if not Result.objects.filter(item=item, confirmed=True).exists():
            messages.error(request, "Confirm the result before publishing.")
        else:
            publish_item(item)
            messages.success(request, "Result published and points distributed.")
    return redirect("result_entry")

@login_required
def result_reset(request):
    if not is_admin(request.user):
        return redirect("home")
    if request.method == "POST":
        item = get_object_or_404(Item, pk=request.POST.get("item_id"))
        reset_item(item)
        messages.success(request, f"{item.code} reset.")
    return render(request, "results/reset.html", {"items": Item.objects.filter(active=True)})

# ---------------- Stage Report ----------------
@login_required
def stage_report(request):
    if not (is_admin(request.user) or is_result_user(request.user)):
        return redirect("home")
    items = Item.objects.filter(active=True).order_by("section", "code")
    item = items.filter(pk=request.GET.get("item_id")).first() if request.GET.get("item_id") else None
    competitors = []
    if item:
        if item.participation_type == "I":
            competitors = Participant.objects.filter(item_links__item=item).select_related("school").order_by("chest_no", "name")
        else:
            competitors = GroupEntry.objects.filter(item=item).select_related("school", "captain").order_by("school__code")
    return render(request, "reports/stage.html", {"items": items, "item": item, "competitors": competitors})

# ---------------- PDF ----------------
def pdf_response(filename, build_fn):
    buf = BytesIO()
    build_fn(buf)
    buf.seek(0)
    return FileResponse(buf, as_attachment=True, filename=filename)

@login_required
def participants_pdf(request):
    if not (is_admin(request.user) or is_school_user(request.user) or is_result_user(request.user)):
        return redirect("home")
    school = current_school(request.user)
    qs = Participant.objects.select_related("school").prefetch_related("item_links__item").order_by("school__code","section","sex","name")
    if school:
        qs = qs.filter(school=school)

    def build(buf):
        from reportlab.lib import colors
        from reportlab.lib.pagesizes import A4, landscape
        from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
        from reportlab.lib.styles import getSampleStyleSheet
        doc = SimpleDocTemplate(buf, pagesize=landscape(A4), rightMargin=18,leftMargin=18,topMargin=18,bottomMargin=18)
        styles = getSampleStyleSheet()
        story = [Paragraph("Marthoma School Kalolsavam - Participants", styles["Title"]), Spacer(1,8)]
        data = [["Chest","Admission","Participant","School","Section","Class","Age","Item1","Item2","Item3","Item4","Item5"]]
        for p in qs:
            names = [x.item.name for x in p.item_links.all()[:5]]
            names += [""] * (5-len(names))
            data.append([p.chest_no or "", p.admission_no, p.name, p.school.name, p.section, p.class_name, p.age, *names])
        table = Table(data, repeatRows=1)
        table.setStyle(TableStyle([
            ("GRID",(0,0),(-1,-1),0.35,colors.grey),
            ("BACKGROUND",(0,0),(-1,0),colors.HexColor("#dce6f1")),
            ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
        ]))
        story.append(table)
        doc.build(story)
    return pdf_response("participants.pdf", build)

@login_required
def results_pdf(request):
    results = _result_queryset(request, published_only=True)

    def build(buf):
        from reportlab.lib import colors
        from reportlab.lib.pagesizes import A4, landscape
        from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
        from reportlab.lib.styles import getSampleStyleSheet
        doc = SimpleDocTemplate(buf, pagesize=landscape(A4))
        styles = getSampleStyleSheet()
        story = [Paragraph("Marthoma School Kalolsavam - Published Results", styles["Title"]), Spacer(1,8)]
        data = [["Place","Chest","Participant / Group","School","Section","Item","Total"]]
        for r in results:
            chest = r.participant.chest_no if r.participant else ""
            data.append([r.place, chest, r.competitor_name, r.competitor_school.name if r.competitor_school else "", r.item.section, r.item.name, str(r.total)])
        table = Table(data, repeatRows=1)
        table.setStyle(TableStyle([
            ("GRID",(0,0),(-1,-1),0.35,colors.grey),
            ("BACKGROUND",(0,0),(-1,0),colors.HexColor("#dce6f1")),
            ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
        ]))
        story.append(table)
        doc.build(story)
    return pdf_response("published_results.pdf", build)

@login_required
def stage_pdf(request):
    if not (is_admin(request.user) or is_result_user(request.user)):
        return redirect("home")
    item = get_object_or_404(Item, pk=request.GET.get("item_id"))
    if item.participation_type == "I":
        competitors = list(Participant.objects.filter(item_links__item=item).select_related("school").order_by("chest_no","name"))
    else:
        competitors = list(GroupEntry.objects.filter(item=item).select_related("school","captain").order_by("school__code"))

    def build(buf):
        from reportlab.lib import colors
        from reportlab.lib.pagesizes import A4
        from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, PageBreak
        from reportlab.lib.styles import getSampleStyleSheet
        doc = SimpleDocTemplate(buf, pagesize=A4, rightMargin=18,leftMargin=18,topMargin=18,bottomMargin=18)
        styles = getSampleStyleSheet()
        story = []
        rows_per_judge_page = 15

        # Three judge copies on three pages.
        for judge in range(1,4):
            story += [
                Paragraph("MARTHOMA SCHOOL KALOLSAVAM", styles["Title"]),
                Paragraph(f"Item Code: {item.code} &nbsp;&nbsp;&nbsp; Item: {item.name}", styles["Normal"]),
                Paragraph(f"Judge {judge} Name: ________________________________", styles["Normal"]),
                Spacer(1,8),
            ]
            data = [["Sl No","Code No","Mark","Remark"]]
            for i in range(rows_per_judge_page):
                code = competitors[i].chest_no if i < len(competitors) and item.participation_type=="I" else (
                    competitors[i].captain.chest_no if i < len(competitors) else ""
                )
                data.append([str(i+1), str(code or ""), "", ""])
            table = Table(data, colWidths=[45,80,65,290], rowHeights=[24]+[28]*rows_per_judge_page)
            table.setStyle(TableStyle([
                ("GRID",(0,0),(-1,-1),0.6,colors.black),
                ("BACKGROUND",(0,0),(-1,0),colors.HexColor("#e8eef7")),
                ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
                ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
            ]))
            story += [table, Spacer(1,12), Paragraph("Signature of the Judge: ________________________________", styles["Normal"])]
            if judge != 3:
                story.append(PageBreak())

        story.append(PageBreak())
        story += [
            Paragraph("STAGE SUMMARY SHEET", styles["Title"]),
            Paragraph(f"Item Code: {item.code} &nbsp;&nbsp;&nbsp; Item: {item.name}", styles["Normal"]),
            Spacer(1,8),
        ]
        data = [["Code No","Chest No","Judge1","Judge2","Judge3","Total","Place"]]
        for i, competitor in enumerate(competitors[:30], start=1):
            chest = competitor.chest_no if item.participation_type=="I" else competitor.captain.chest_no
            data.append([i, chest or "", "", "", "", "", ""])
        table = Table(data, colWidths=[55,65,70,70,70,70,55])
        table.setStyle(TableStyle([
            ("GRID",(0,0),(-1,-1),0.6,colors.black),
            ("BACKGROUND",(0,0),(-1,0),colors.HexColor("#e8eef7")),
            ("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
        ]))
        story += [table, Spacer(1,12), Paragraph("Signature of Stage Manager: ________________________________", styles["Normal"])]
        doc.build(story)
    return pdf_response("stage_report.pdf", build)

# ---------------- Result points detail ----------------
@login_required
def point_detail(request, result_id):
    result = get_object_or_404(Result.objects.select_related("item","participant__school","group_entry__school"), pk=result_id)
    points = SchoolPoint.objects.filter(result=result)
    participant_points = ParticipantPoint.objects.filter(result=result)
    return render(request, "results/point_detail.html", {
        "result": result,
        "school_points": points,
        "participant_points": participant_points,
    })
