from fastapi import APIRouter, Depends, HTTPException, Query, UploadFile, File, Request
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, and_, or_
from sqlalchemy.exc import IntegrityError
from typing import Optional, List
import os, uuid, shutil
from datetime import date

from app.core.deps import DB, AdminUser, require_admin
from app.models.user import User
from app.models.school import School, Organization, SchoolOrgAffiliation
from app.models.academic import Subject, Grade, Topic, TopicLevel, Question, Package, AnswerOption, SubjectGrade
from app.models.game import Game, GameType, GameAsset, GameLevel as GameLevelModel, GameTopic, GameCategory
from app.models.progress import AuditLog
from app.models.payment import Subscription, Payment
from app.schemas.user import UserOut, UserListOut, AdminUpdateUserRequest
from app.schemas.academic import SubjectCreateRequest, TopicCreateRequest, TopicLevelCreateRequest
from app.schemas.game import GameCreateRequest, GameAssetCreateRequest, GameTypeCreateRequest, GameTypeUpdateRequest

router = APIRouter(prefix="/admin", tags=["Admin"], dependencies=[Depends(require_admin)])


# ===================== DASHBOARD =====================

@router.get("/dashboard", summary="Admin Dashboard Stats")
async def get_dashboard(db: DB):
    """Get platform-wide statistics for the admin dashboard."""
    total_users = (await db.execute(select(func.count(User.id)))).scalar()
    total_students = (await db.execute(select(func.count(User.id)).where(User.role == "student"))).scalar()
    total_schools = (await db.execute(select(func.count(School.id)))).scalar()
    total_games = (await db.execute(select(func.count(Game.id)))).scalar()
    total_topics = (await db.execute(select(func.count(Topic.id)))).scalar()
    active_subscriptions = (
        await db.execute(
            select(func.count(Subscription.id)).where(Subscription.status == "active")
        )
    ).scalar()
    pending_schools = (
        await db.execute(select(func.count(School.id)).where(School.is_verified == False))
    ).scalar()
    pending_orgs = (
        await db.execute(select(func.count(Organization.id)).where(Organization.is_verified == False))
    ).scalar()

    return {
        "total_users": total_users,
        "total_students": total_students,
        "total_schools": total_schools,
        "total_games": total_games,
        "total_topics": total_topics,
        "active_subscriptions": active_subscriptions,
        "pending_verifications": pending_schools + pending_orgs,
        "pending_schools": pending_schools,
        "pending_organizations": pending_orgs,
    }


# ===================== USERS =====================

@router.get("/users", summary="List All Users")
async def list_users(
    db: DB,
    role: Optional[str] = None,
    search: Optional[str] = None,
    is_active: Optional[bool] = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
):
    query = select(User)
    if role:
        query = query.where(User.role == role)
    if search:
        query = query.where(
            or_(
                User.email.ilike(f"%{search}%"),
                User.first_name.ilike(f"%{search}%"),
                User.last_name.ilike(f"%{search}%"),
                User.stem_id.ilike(f"%{search}%"),
            )
        )
    if is_active is not None:
        query = query.where(User.is_active == is_active)

    total = (await db.execute(select(func.count()).select_from(query.subquery()))).scalar()
    result = await db.execute(query.offset((page - 1) * page_size).limit(page_size))
    users = result.scalars().all()

    return {
        "items": [UserListOut.model_validate(u) for u in users],
        "total": total,
        "page": page,
        "page_size": page_size,
        "total_pages": (total + page_size - 1) // page_size,
    }


@router.get("/users/{user_id}", response_model=UserOut, summary="Get User Details")
async def get_user(user_id: int, db: DB):
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user


@router.patch("/users/{user_id}", summary="Update User")
async def update_user(user_id: int, data: AdminUpdateUserRequest, db: DB, current_user: AdminUser):
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")

    for field, value in data.model_dump(exclude_none=True).items():
        setattr(user, field, value)

    await db.commit()
    return {"message": "User updated", "user_id": user_id}


@router.delete("/users/{user_id}", summary="Deactivate User")
async def deactivate_user(user_id: int, db: DB, current_user: AdminUser):
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    user.is_active = False
    await db.commit()
    return {"message": "User deactivated"}


# ===================== SCHOOLS =====================

@router.get("/schools", summary="List All Schools")
async def list_schools(
    db: DB,
    verified: Optional[bool] = None,
    search: Optional[str] = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
):
    query = select(School)
    if verified is not None:
        query = query.where(School.is_verified == verified)
    if search:
        query = query.where(School.school_name.ilike(f"%{search}%"))

    total = (await db.execute(select(func.count()).select_from(query.subquery()))).scalar()
    result = await db.execute(query.offset((page - 1) * page_size).limit(page_size))
    schools = result.scalars().all()
    return {"items": schools, "total": total, "page": page, "page_size": page_size}


@router.post("/schools/{school_id}/verify", summary="Verify School Account")
async def verify_school(school_id: int, db: DB, current_user: AdminUser):
    result = await db.execute(select(School).where(School.id == school_id))
    school = result.scalar_one_or_none()
    if not school:
        raise HTTPException(status_code=404, detail="School not found")

    from datetime import datetime, timezone
    school.is_verified = True
    school.verified_by_user_id = current_user.id
    school.verified_at = datetime.now(timezone.utc)
    await db.commit()
    return {"message": f"School '{school.school_name}' verified successfully"}


@router.post("/schools/{school_id}/reject", summary="Reject School Registration")
async def reject_school(school_id: int, db: DB, current_user: AdminUser):
    result = await db.execute(select(School).where(School.id == school_id))
    school = result.scalar_one_or_none()
    if not school:
        raise HTTPException(status_code=404, detail="School not found")
    school.is_active = False
    await db.commit()
    return {"message": "School registration rejected"}


# ===================== ORGANIZATIONS =====================

@router.get("/organizations", summary="List All Organizations")
async def list_organizations(db: DB, verified: Optional[bool] = None):
    query = select(Organization)
    if verified is not None:
        query = query.where(Organization.is_verified == verified)
    result = await db.execute(query)
    return result.scalars().all()


@router.post("/organizations/{org_id}/verify", summary="Verify Organization")
async def verify_organization(org_id: int, db: DB, current_user: AdminUser):
    result = await db.execute(select(Organization).where(Organization.id == org_id))
    org = result.scalar_one_or_none()
    if not org:
        raise HTTPException(status_code=404, detail="Organization not found")
    from datetime import datetime, timezone
    org.is_verified = True
    org.verified_by_user_id = current_user.id
    org.verified_at = datetime.now(timezone.utc)
    await db.commit()
    return {"message": f"Organization '{org.org_name}' verified"}


# ===================== SUBJECTS & ACADEMIC =====================

@router.get("/subjects", summary="List Subjects")
async def list_subjects(db: DB):
    result = await db.execute(select(Subject).order_by(Subject.name))
    subjects = result.scalars().all()
    return [
        {
            "id": s.id,
            "name": s.name,
            "slug": s.slug,
            "description": s.description,
            "icon_url": s.icon_url,
            "color_hex": s.color_hex,
            "is_active": s.is_active,
        }
        for s in subjects
    ]


@router.post("/subjects", summary="Create Subject", status_code=201)
async def create_subject(data: SubjectCreateRequest, db: DB):
    subject = Subject(**data.model_dump())
    db.add(subject)
    await db.commit()
    await db.refresh(subject)
    return {
        "id": subject.id, "name": subject.name, "slug": subject.slug,
        "description": subject.description, "icon_url": subject.icon_url,
        "color_hex": subject.color_hex, "is_active": subject.is_active,
    }


@router.patch("/subjects/{subject_id}", summary="Update Subject")
async def update_subject(subject_id: int, data: dict, db: DB):
    result = await db.execute(select(Subject).where(Subject.id == subject_id))
    subject = result.scalar_one_or_none()
    if not subject:
        raise HTTPException(status_code=404, detail="Subject not found")
    allowed = {"name", "slug", "description", "icon_url", "color_hex", "is_active"}
    for field, value in data.items():
        if field in allowed:
            setattr(subject, field, value)
    await db.commit()
    await db.refresh(subject)
    return {
        "id": subject.id, "name": subject.name, "slug": subject.slug,
        "description": subject.description, "icon_url": subject.icon_url,
        "color_hex": subject.color_hex, "is_active": subject.is_active,
    }


@router.delete("/subjects/{subject_id}", summary="Delete Subject")
async def delete_subject(subject_id: int, db: DB):
    result = await db.execute(select(Subject).where(Subject.id == subject_id))
    subject = result.scalar_one_or_none()
    if not subject:
        raise HTTPException(status_code=404, detail="Subject not found")
    subject.is_active = False
    await db.commit()
    return {"message": "Subject deactivated"}


@router.get("/subjects/{subject_id}/grades", summary="List Grades for a Subject")
async def list_subject_grades(subject_id: int, db: DB):
    """Get all grade combinations (SubjectGrades) for this subject, with grade details."""
    result = await db.execute(
        select(SubjectGrade, Grade)
        .join(Grade, Grade.id == SubjectGrade.grade_id)
        .where(SubjectGrade.subject_id == subject_id)
        .order_by(Grade.level_order)
    )
    rows = result.all()
    return [
        {
            "id": sg.id,
            "subject_id": sg.subject_id,
            "grade_id": sg.grade_id,
            "grade_name": g.name,
            "grade_slug": g.slug,
            "level_order": g.level_order,
            "is_active": sg.is_active,
        }
        for sg, g in rows
    ]


@router.post("/subjects/{subject_id}/grades", summary="Link Subject to Grade", status_code=201)
async def link_subject_grade(subject_id: int, data: dict, db: DB):
    """Create or reactivate a subject-grade link."""
    grade_id = data.get("grade_id")
    if not grade_id:
        raise HTTPException(status_code=400, detail="grade_id is required")

    # Validate subject + grade exist
    s_res = await db.execute(select(Subject).where(Subject.id == subject_id))
    if not s_res.scalar_one_or_none():
        raise HTTPException(status_code=404, detail="Subject not found")
    g_res = await db.execute(select(Grade).where(Grade.id == grade_id))
    grade = g_res.scalar_one_or_none()
    if not grade:
        raise HTTPException(status_code=404, detail="Grade not found")

    # Check if link already exists
    existing = await db.execute(
        select(SubjectGrade).where(SubjectGrade.subject_id == subject_id, SubjectGrade.grade_id == grade_id)
    )
    sg = existing.scalar_one_or_none()
    if sg:
        if not sg.is_active:
            sg.is_active = True
            await db.commit()
            await db.refresh(sg)
        return {"id": sg.id, "subject_id": sg.subject_id, "grade_id": sg.grade_id,
                "grade_name": grade.name, "level_order": grade.level_order, "is_active": sg.is_active}

    sg = SubjectGrade(subject_id=subject_id, grade_id=grade_id, is_active=True)
    db.add(sg)
    await db.commit()
    await db.refresh(sg)
    return {"id": sg.id, "subject_id": sg.subject_id, "grade_id": sg.grade_id,
            "grade_name": grade.name, "level_order": grade.level_order, "is_active": sg.is_active}


@router.get("/subjects/{subject_id}/topics", summary="List Topics for a Subject")
async def list_subject_topics(subject_id: int, db: DB):
    """Return all topics under a subject, grouped with their subject_grade info."""
    result = await db.execute(
        select(Topic, SubjectGrade, Grade)
        .join(SubjectGrade, SubjectGrade.id == Topic.subject_grade_id)
        .join(Grade, Grade.id == SubjectGrade.grade_id)
        .where(SubjectGrade.subject_id == subject_id)
        .order_by(Grade.level_order, Topic.sequence_order)
    )
    rows = result.all()
    return [
        {
            "id": t.id,
            "subject_grade_id": t.subject_grade_id,
            "grade_id": sg.grade_id,
            "grade_name": g.name,
            "grade_level": g.level_order,
            "package_id": t.package_id,
            "name": t.name,
            "slug": t.slug,
            "description": t.description,
            "thumbnail_url": t.thumbnail_url,
            "sequence_order": t.sequence_order,
            "total_levels": t.total_levels,
            "is_offline_available": t.is_offline_available,
            "is_active": t.is_active,
        }
        for t, sg, g in rows
    ]


@router.patch("/topics/{topic_id}", summary="Update Topic")
async def update_topic(topic_id: int, data: dict, db: DB):
    result = await db.execute(select(Topic).where(Topic.id == topic_id))
    topic = result.scalar_one_or_none()
    if not topic:
        raise HTTPException(status_code=404, detail="Topic not found")
    allowed = {"name", "slug", "description", "thumbnail_url", "sequence_order",
               "total_levels", "is_offline_available", "is_active", "package_id"}
    for field, value in data.items():
        if field in allowed:
            setattr(topic, field, value)
    await db.commit()
    await db.refresh(topic)
    return {"id": topic.id, "name": topic.name, "slug": topic.slug,
            "description": topic.description, "package_id": topic.package_id,
            "sequence_order": topic.sequence_order, "total_levels": topic.total_levels,
            "is_offline_available": topic.is_offline_available, "is_active": topic.is_active}


@router.delete("/topics/{topic_id}", summary="Delete Topic")
async def delete_topic(topic_id: int, db: DB):
    result = await db.execute(select(Topic).where(Topic.id == topic_id))
    topic = result.scalar_one_or_none()
    if not topic:
        raise HTTPException(status_code=404, detail="Topic not found")
    topic.is_active = False
    await db.commit()
    return {"message": "Topic deactivated"}


@router.get("/grades", summary="List Grades")
async def list_grades(db: DB):
    result = await db.execute(select(Grade).order_by(Grade.level_order))
    grades = result.scalars().all()
    return [
        {
            "id": g.id,
            "name": g.name,
            "slug": g.slug,
            "description": g.description,
            "level_order": g.level_order,
            "is_active": g.is_active,
        }
        for g in grades
    ]


@router.post("/grades", summary="Create Grade", status_code=201)
async def create_grade(data: dict, db: DB):
    if not data.get("name") or not data.get("slug"):
        raise HTTPException(status_code=400, detail="name and slug are required")
    grade = Grade(
        name=data["name"],
        slug=data["slug"],
        description=data.get("description"),
        level_order=data.get("level_order", 1),
        is_active=True,
    )
    db.add(grade)
    await db.commit()
    await db.refresh(grade)
    return {
        "id": grade.id, "name": grade.name, "slug": grade.slug,
        "description": grade.description, "level_order": grade.level_order,
        "is_active": grade.is_active,
    }


@router.patch("/grades/{grade_id}", summary="Update Grade")
async def update_grade(grade_id: int, data: dict, db: DB):
    result = await db.execute(select(Grade).where(Grade.id == grade_id))
    grade = result.scalar_one_or_none()
    if not grade:
        raise HTTPException(status_code=404, detail="Grade not found")
    for field in {"name", "slug", "description", "level_order", "is_active"}:
        if field in data:
            setattr(grade, field, data[field])
    await db.commit()
    await db.refresh(grade)
    return {
        "id": grade.id, "name": grade.name, "slug": grade.slug,
        "description": grade.description, "level_order": grade.level_order,
        "is_active": grade.is_active,
    }


@router.get("/grades/{grade_id}/subjects", summary="List Subjects linked to a Grade")
async def list_grade_subjects(grade_id: int, db: DB):
    result = await db.execute(
        select(SubjectGrade, Subject)
        .join(Subject, Subject.id == SubjectGrade.subject_id)
        .where(SubjectGrade.grade_id == grade_id)
        .order_by(Subject.name)
    )
    rows = result.all()
    return [
        {
            "id": sg.id,
            "grade_id": sg.grade_id,
            "subject_id": sg.subject_id,
            "subject_name": s.name,
            "subject_slug": s.slug,
            "subject_color": s.color_hex,
            "is_active": sg.is_active,
        }
        for sg, s in rows
    ]


@router.post("/grades/{grade_id}/subjects", summary="Link Grade to Subject", status_code=201)
async def link_grade_subject(grade_id: int, data: dict, db: DB):
    subject_id = data.get("subject_id")
    if not subject_id:
        raise HTTPException(status_code=400, detail="subject_id is required")

    g_res = await db.execute(select(Grade).where(Grade.id == grade_id))
    if not g_res.scalar_one_or_none():
        raise HTTPException(status_code=404, detail="Grade not found")
    s_res = await db.execute(select(Subject).where(Subject.id == subject_id))
    subject = s_res.scalar_one_or_none()
    if not subject:
        raise HTTPException(status_code=404, detail="Subject not found")

    existing = await db.execute(
        select(SubjectGrade).where(SubjectGrade.grade_id == grade_id, SubjectGrade.subject_id == subject_id)
    )
    sg = existing.scalar_one_or_none()
    if sg:
        if not sg.is_active:
            sg.is_active = True
            await db.commit()
        return {"id": sg.id, "grade_id": sg.grade_id, "subject_id": sg.subject_id,
                "subject_name": subject.name, "subject_color": subject.color_hex, "is_active": sg.is_active}

    sg = SubjectGrade(grade_id=grade_id, subject_id=subject_id, is_active=True)
    db.add(sg)
    await db.commit()
    await db.refresh(sg)
    return {"id": sg.id, "grade_id": sg.grade_id, "subject_id": sg.subject_id,
            "subject_name": subject.name, "subject_color": subject.color_hex, "is_active": sg.is_active}


@router.delete("/grades/{grade_id}/subjects/{subject_id}", summary="Unlink Grade from Subject")
async def unlink_grade_subject(grade_id: int, subject_id: int, db: DB):
    result = await db.execute(
        select(SubjectGrade).where(SubjectGrade.grade_id == grade_id, SubjectGrade.subject_id == subject_id)
    )
    sg = result.scalar_one_or_none()
    if not sg:
        raise HTTPException(status_code=404, detail="Link not found")
    sg.is_active = False
    await db.commit()
    return {"message": "Unlinked"}


@router.get("/topics", summary="List Topics")
async def list_topics(db: DB, subject_id: Optional[int] = None, grade_id: Optional[int] = None):
    query = (
        select(Topic, SubjectGrade, Grade)
        .join(SubjectGrade, SubjectGrade.id == Topic.subject_grade_id)
        .join(Grade, Grade.id == SubjectGrade.grade_id)
    )
    if subject_id:
        query = query.where(SubjectGrade.subject_id == subject_id)
    if grade_id:
        query = query.where(SubjectGrade.grade_id == grade_id)
    result = await db.execute(query)
    rows = result.all()
    return [
        {
            "id": t.id, "subject_grade_id": t.subject_grade_id,
            "subject_id": sg.subject_id, "grade_id": sg.grade_id,
            "grade_name": g.name, "grade_level": g.level_order,
            "package_id": t.package_id, "name": t.name, "slug": t.slug,
            "description": t.description, "thumbnail_url": t.thumbnail_url,
            "sequence_order": t.sequence_order, "total_levels": t.total_levels,
            "is_offline_available": t.is_offline_available, "is_active": t.is_active,
        }
        for t, sg, g in rows
    ]


@router.post("/topics", summary="Create Topic", status_code=201)
async def create_topic(data: TopicCreateRequest, db: DB):
    topic = Topic(**data.model_dump())
    db.add(topic)
    await db.commit()
    await db.refresh(topic)
    return {
        "id": topic.id, "subject_grade_id": topic.subject_grade_id, "package_id": topic.package_id,
        "name": topic.name, "slug": topic.slug, "description": topic.description,
        "thumbnail_url": topic.thumbnail_url, "sequence_order": topic.sequence_order,
        "total_levels": topic.total_levels, "is_offline_available": topic.is_offline_available,
        "is_active": topic.is_active,
    }


@router.post("/topic-levels", summary="Create Topic Level", status_code=201)
async def create_topic_level(data: TopicLevelCreateRequest, db: DB):
    level = TopicLevel(**data.model_dump())
    db.add(level)
    await db.commit()
    await db.refresh(level)
    return level


@router.get("/topics/{topic_id}/levels", summary="List Levels for an Academic Topic")
async def list_levels_for_topic(topic_id: int, db: DB):
    """Return all TopicLevels under a given Topic, ordered by level_number."""
    result = await db.execute(
        select(TopicLevel)
        .where(TopicLevel.topic_id == topic_id)
        .order_by(TopicLevel.level_number)
    )
    levels = result.scalars().all()
    return [
        {
            "id": l.id, "topic_id": l.topic_id,
            "level_number": l.level_number, "name": l.name,
            "description": l.description, "instructions": l.instructions,
            "xp_reward": l.xp_reward, "passing_score": l.passing_score,
            "time_limit_seconds": l.time_limit_seconds, "stars_max": l.stars_max,
            "is_active": l.is_active,
        }
        for l in levels
    ]


# ===================== GAMES =====================

@router.get("/games", summary="List Games")
async def list_games(db: DB):
    from sqlalchemy.orm import selectinload
    result = await db.execute(
        select(Game).options(selectinload(Game.category)).order_by(Game.id)
    )
    games = result.scalars().all()
    return [
        {
            "id": g.id,
            "name": g.name,
            "slug": g.slug,
            "description": g.description,
            "instructions": g.instructions,
            "intro_video_url": g.intro_video_url,
            "artwork_url": g.artwork_url,
            "game_type_id": g.game_type_id,
            "category_id": g.category_id,
            "category_name": g.category.name if g.category else None,
            "category_icon": g.category.icon if g.category else None,
            "category_color": g.category.color_hex if g.category else None,
            "subject_grade_id": g.subject_grade_id,
            "is_active": g.is_active,
            "created_at": g.created_at.isoformat() if g.created_at else None,
        }
        for g in games
    ]


@router.post("/games", summary="Create Game", status_code=201)
async def create_game(data: GameCreateRequest, db: DB):
    from sqlalchemy.orm import selectinload
    game = Game(**data.model_dump())
    db.add(game)
    await db.commit()
    res = await db.execute(
        select(Game).options(selectinload(Game.category)).where(Game.id == game.id)
    )
    game = res.scalar_one()
    return {
        "id": game.id,
        "name": game.name,
        "slug": game.slug,
        "description": game.description,
        "instructions": game.instructions,
        "intro_video_url": game.intro_video_url,
        "artwork_url": game.artwork_url,
        "game_type_id": game.game_type_id,
        "category_id": game.category_id,
        "category_name": game.category.name if game.category else None,
        "category_icon": game.category.icon if game.category else None,
        "category_color": game.category.color_hex if game.category else None,
        "subject_grade_id": game.subject_grade_id,
        "is_active": game.is_active,
        "created_at": game.created_at.isoformat() if game.created_at else None,
    }


@router.patch("/games/{game_id}", summary="Update Game")
async def update_game(game_id: int, data: GameCreateRequest, db: DB):
    from sqlalchemy.orm import selectinload
    result = await db.execute(select(Game).where(Game.id == game_id))
    game = result.scalar_one_or_none()
    if not game:
        raise HTTPException(status_code=404, detail="Game not found")
    for field, value in data.model_dump(exclude_none=True).items():
        setattr(game, field, value)
    await db.commit()
    res = await db.execute(
        select(Game).options(selectinload(Game.category)).where(Game.id == game_id)
    )
    game = res.scalar_one()
    return {
        "id": game.id,
        "name": game.name,
        "slug": game.slug,
        "description": game.description,
        "instructions": game.instructions,
        "intro_video_url": game.intro_video_url,
        "artwork_url": game.artwork_url,
        "game_type_id": game.game_type_id,
        "category_id": game.category_id,
        "category_name": game.category.name if game.category else None,
        "category_icon": game.category.icon if game.category else None,
        "category_color": game.category.color_hex if game.category else None,
        "subject_grade_id": game.subject_grade_id,
        "is_active": game.is_active,
        "created_at": game.created_at.isoformat() if game.created_at else None,
    }


@router.delete("/games/{game_id}", summary="Delete Game")
async def delete_game(game_id: int, db: DB):
    result = await db.execute(select(Game).where(Game.id == game_id))
    game = result.scalar_one_or_none()
    if not game:
        raise HTTPException(status_code=404, detail="Game not found")
    game.is_active = False
    await db.commit()
    return {"message": "Game deactivated"}


# ===================== GAME CATEGORIES =====================

@router.get("/game-categories", summary="List Game Categories")
async def list_game_categories(db: DB):
    result = await db.execute(
        select(GameCategory).where(GameCategory.is_active == True).order_by(GameCategory.name)
    )
    cats = result.scalars().all()
    return [
        {
            "id": c.id, "name": c.name, "slug": c.slug,
            "description": c.description, "icon": c.icon,
            "color_hex": c.color_hex, "thumbnail_url": c.thumbnail_url, "is_active": c.is_active,
        }
        for c in cats
    ]


@router.post("/game-categories", summary="Create Game Category", status_code=201)
async def create_game_category(data: dict, db: DB):
    name = data.get("name", "").strip()
    slug = data.get("slug", "").strip()
    if not name or not slug:
        raise HTTPException(status_code=400, detail="name and slug are required")
    existing = await db.execute(select(GameCategory).where(GameCategory.slug == slug))
    if existing.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="A category with this slug already exists")
    cat = GameCategory(
        name=name, slug=slug,
        description=data.get("description"),
        icon=data.get("icon"),
        color_hex=data.get("color_hex", "#4F46E5"),
        thumbnail_url=data.get("thumbnail_url"),
        is_active=True,
    )
    db.add(cat)
    await db.commit()
    await db.refresh(cat)
    return {
        "id": cat.id, "name": cat.name, "slug": cat.slug,
        "description": cat.description, "icon": cat.icon,
        "color_hex": cat.color_hex, "thumbnail_url": cat.thumbnail_url, "is_active": cat.is_active,
    }


@router.patch("/game-categories/{cat_id}", summary="Update Game Category")
async def update_game_category(cat_id: int, data: dict, db: DB):
    result = await db.execute(select(GameCategory).where(GameCategory.id == cat_id))
    cat = result.scalar_one_or_none()
    if not cat:
        raise HTTPException(status_code=404, detail="Category not found")
    for field in ("name", "slug", "description", "icon", "color_hex", "thumbnail_url", "is_active"):
        if field in data:
            setattr(cat, field, data[field])
    await db.commit()
    await db.refresh(cat)
    return {
        "id": cat.id, "name": cat.name, "slug": cat.slug,
        "description": cat.description, "icon": cat.icon,
        "color_hex": cat.color_hex, "thumbnail_url": cat.thumbnail_url, "is_active": cat.is_active,
    }


@router.delete("/game-categories/{cat_id}", summary="Delete Game Category")
async def delete_game_category(cat_id: int, db: DB):
    result = await db.execute(select(GameCategory).where(GameCategory.id == cat_id))
    cat = result.scalar_one_or_none()
    if not cat:
        raise HTTPException(status_code=404, detail="Category not found")
    cat.is_active = False
    await db.commit()
    return {"message": "Category deactivated"}


# ===================== GAME TYPES =====================

@router.get("/game-types", summary="List Game Types")
async def list_game_types(db: DB):
    result = await db.execute(select(GameType).where(GameType.is_active == True))
    types = result.scalars().all()
    return [
        {
            "id": t.id,
            "name": t.name,
            "slug": t.slug,
            "description": t.description,
            "icon_url": t.icon_url,
            "is_active": t.is_active,
        }
        for t in types
    ]


@router.post("/game-types", summary="Create Game Type", status_code=201)
async def create_game_type(data: GameTypeCreateRequest, db: DB):
    """Create a new game type for the platform."""
    # Check if slug already exists
    existing = await db.execute(
        select(GameType).where(GameType.slug == data.slug)
    )
    if existing.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="Game type slug already exists")
    
    game_type = GameType(
        name=data.name,
        slug=data.slug,
        description=data.description,
        icon_url=data.icon_url,
        is_active=True,
    )
    db.add(game_type)
    await db.commit()
    await db.refresh(game_type)
    
    return {
        "id": game_type.id,
        "name": game_type.name,
        "slug": game_type.slug,
        "description": game_type.description,
        "icon_url": game_type.icon_url,
        "is_active": game_type.is_active,
        "created_at": game_type.created_at.isoformat() if game_type.created_at else None,
    }


@router.put("/game-types/{game_type_id}", summary="Update Game Type")
async def update_game_type(game_type_id: int, data: GameTypeUpdateRequest, db: DB):
    """Update an existing game type."""
    result = await db.execute(select(GameType).where(GameType.id == game_type_id))
    game_type = result.scalar_one_or_none()
    if not game_type:
        raise HTTPException(status_code=404, detail="Game type not found")
    
    # Check if new slug is unique (if provided)
    if data.slug and data.slug != game_type.slug:
        existing = await db.execute(
            select(GameType).where(GameType.slug == data.slug)
        )
        if existing.scalar_one_or_none():
            raise HTTPException(status_code=400, detail="Game type slug already exists")
        game_type.slug = data.slug
    
    if data.name:
        game_type.name = data.name
    if data.description is not None:
        game_type.description = data.description
    if data.icon_url is not None:
        game_type.icon_url = data.icon_url
    if data.is_active is not None:
        game_type.is_active = data.is_active
    
    await db.commit()
    await db.refresh(game_type)
    
    return {
        "id": game_type.id,
        "name": game_type.name,
        "slug": game_type.slug,
        "description": game_type.description,
        "icon_url": game_type.icon_url,
        "is_active": game_type.is_active,
        "updated_at": game_type.updated_at.isoformat() if game_type.updated_at else None,
    }


@router.delete("/game-types/{game_type_id}", summary="Delete Game Type")
async def delete_game_type(game_type_id: int, db: DB):
    """Soft delete a game type by deactivating it."""
    result = await db.execute(select(GameType).where(GameType.id == game_type_id))
    game_type = result.scalar_one_or_none()
    if not game_type:
        raise HTTPException(status_code=404, detail="Game type not found")
    
    game_type.is_active = False
    await db.commit()
    
    return {"message": "Game type deactivated"}


@router.get("/game-assets", summary="List Game Assets")
async def list_game_assets(db: DB, game_type_id: Optional[int] = None):
    query = select(GameAsset).where(GameAsset.is_active == True)
    if game_type_id:
        query = query.where(GameAsset.game_type_id == game_type_id)
    result = await db.execute(query)
    assets = result.scalars().all()
    return [
        {
            "id": a.id,
            "game_type_id": a.game_type_id,
            "name": a.name,
            "slug": a.slug,
            "svg_url": a.svg_url,
            "image_url": a.image_url,
            "rive_url": a.rive_url,
            "sound_url": a.sound_url,
            "model_3d_url": a.model_3d_url,
            "asset_type": a.asset_type,
            "asset_group": a.asset_group,
            "monetary_value": str(a.monetary_value) if a.monetary_value else "0",
            "quantity_default": a.quantity_default,
            "is_active": a.is_active,
        }
        for a in assets
    ]


@router.post("/game-assets", summary="Create Game Asset", status_code=201)
async def create_game_asset(data: GameAssetCreateRequest, db: DB):
    asset = GameAsset(**data.model_dump())
    db.add(asset)
    await db.commit()
    await db.refresh(asset)
    return {
        "id": asset.id,
        "game_type_id": asset.game_type_id,
        "name": asset.name,
        "slug": asset.slug,
        "image_url": asset.image_url,
        "asset_type": asset.asset_type,
        "asset_group": asset.asset_group,
        "is_active": asset.is_active,
    }


ALLOWED_MIME_TYPES = {
    "image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml",
    "audio/mpeg", "audio/ogg", "audio/wav",
    "video/mp4", "video/webm",
    "application/octet-stream",   # .riv (Rive animations) and 3D models
    "model/gltf-binary", "model/gltf+json",
}
MAX_UPLOAD_BYTES = 20 * 1024 * 1024  # 20 MB


@router.post("/upload-asset", summary="Upload an Asset File", status_code=201)
async def upload_asset_file(request: Request, file: UploadFile = File(...)):
    """
    Upload a binary asset file (image, audio, video, rive, model).
    Returns the full public URL e.g. http://localhost:8000/uploads/<filename>.
    Max size: 20 MB.
    """
    if file.content_type not in ALLOWED_MIME_TYPES:
        raise HTTPException(
            status_code=415,
            detail=f"Unsupported file type '{file.content_type}'. Allowed: images, audio, video, rive, glTF."
        )

    # Read with size guard
    contents = await file.read(MAX_UPLOAD_BYTES + 1)
    if len(contents) > MAX_UPLOAD_BYTES:
        raise HTTPException(status_code=413, detail="File exceeds 20 MB limit.")

    # Sanitise filename and make it unique
    original_ext = os.path.splitext(file.filename or "asset")[1].lower() or ".bin"
    safe_name = f"{uuid.uuid4().hex}{original_ext}"

    uploads_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..", "uploads")
    os.makedirs(uploads_dir, exist_ok=True)
    dest = os.path.join(uploads_dir, safe_name)
    with open(dest, "wb") as f:
        f.write(contents)

    base = str(request.base_url).rstrip("/")
    return {"url": f"{base}/uploads/{safe_name}", "filename": safe_name, "size": len(contents)}



@router.get("/games/{game_id}/levels", summary="List Game Levels")
async def list_game_levels(game_id: int, db: DB):
    from app.models.game import GameLevel
    result = await db.execute(
        select(GameLevel).where(GameLevel.game_id == game_id, GameLevel.is_active == True).order_by(GameLevel.sequence_order)
    )
    levels = result.scalars().all()
    return [
        {
            "id": l.id,
            "game_id": l.game_id,
            "topic_level_id": l.topic_level_id,
            "title": l.title,
            "instructions": l.instructions,
            "description": l.description,
            "tag": l.tag,
            "sequence_order": l.sequence_order,
            "is_active": l.is_active,
        }
        for l in levels
    ]


@router.post("/game-levels", summary="Create Game Level", status_code=201)
async def create_game_level(data: dict, db: DB):
    from app.models.game import GameLevel
    level = GameLevel(**data)
    db.add(level)
    await db.commit()
    await db.refresh(level)
    return {
        "id": level.id,
        "game_id": level.game_id,
        "topic_level_id": level.topic_level_id,
        "title": level.title,
        "instructions": level.instructions,
        "description": level.description,
        "tag": level.tag,
        "sequence_order": level.sequence_order,
        "is_active": level.is_active,
    }


@router.patch("/game-levels/{level_id}", summary="Update Game Level")
async def update_game_level(level_id: int, data: dict, db: DB):
    from app.models.game import GameLevel
    result = await db.execute(select(GameLevel).where(GameLevel.id == level_id))
    level = result.scalar_one_or_none()
    if not level:
        raise HTTPException(status_code=404, detail="Game level not found")
    
    for field, value in data.items():
        if hasattr(level, field):
            setattr(level, field, value)
    
    await db.commit()
    await db.refresh(level)
    return {
        "id": level.id,
        "game_id": level.game_id,
        "topic_level_id": level.topic_level_id,
        "title": level.title,
        "instructions": level.instructions,
        "description": level.description,
        "tag": level.tag,
        "sequence_order": level.sequence_order,
        "is_active": level.is_active,
    }


@router.delete("/game-levels/{level_id}", summary="Delete Game Level")
async def delete_game_level(level_id: int, db: DB):
    from app.models.game import GameLevel
    result = await db.execute(select(GameLevel).where(GameLevel.id == level_id))
    level = result.scalar_one_or_none()
    if not level:
        raise HTTPException(status_code=404, detail="Game level not found")
    
    level.is_active = False
    await db.commit()
    return {"message": "Game level deactivated"}


# ===================== QUESTIONS =====================

@router.get("/questions", summary="List Questions")
async def list_questions(db: DB, level_id: Optional[int] = None, topic_id: Optional[int] = None):
    query = select(Question).where(Question.is_active == True)
    if level_id:
        query = query.where(Question.level_id == level_id)
    result = await db.execute(query.order_by(Question.sequence_order))
    questions = result.scalars().all()
    return [
        {
            "id": q.id,
            "level_id": q.level_id,
            "question_text": q.question_text,
            "question_type": q.question_type,
            "difficulty": q.difficulty,
            "sequence_order": q.sequence_order,
            "time_limit_seconds": q.time_limit_seconds,
            "points": q.points,
            "hint_text": q.hint_text,
            "explanation": q.explanation,
            "media_url": q.media_url,
            "is_verified": q.is_verified,
            "is_active": q.is_active,
        }
        for q in questions
    ]


@router.post("/questions", summary="Create Question", status_code=201)
async def create_question(data: dict, db: DB, current_user: AdminUser):
    from app.models.academic import AnswerOption
    
    answers_data = data.pop("answers", [])
    question = Question(**data, is_verified=True, verified_by_user_id=current_user.id)
    db.add(question)
    await db.flush()
    
    for i, ans in enumerate(answers_data):
        answer = AnswerOption(
            question_id=question.id,
            answer_text=ans.get("answer_text"),
            is_correct=ans.get("is_correct", False),
            explanation=ans.get("explanation"),
            display_order=i + 1,
            asset_2d_id=ans.get("asset_2d_id"),
            asset_3d_id=ans.get("asset_3d_id"),
            asset_vr_id=ans.get("asset_vr_id"),
            asset_group=ans.get("asset_group"),
            monetary_value=ans.get("monetary_value"),
            quantity=ans.get("quantity", 1),
        )
        db.add(answer)
    
    await db.commit()
    await db.refresh(question)
    return {
        "id": question.id,
        "level_id": question.level_id,
        "question_text": question.question_text,
        "question_type": question.question_type,
        "difficulty": question.difficulty,
        "sequence_order": question.sequence_order,
        "time_limit_seconds": question.time_limit_seconds,
        "points": question.points,
        "hint_text": question.hint_text,
        "explanation": question.explanation,
        "media_url": question.media_url,
        "is_verified": question.is_verified,
        "is_active": question.is_active,
    }


@router.patch("/questions/{question_id}", summary="Update Question")
async def update_question(question_id: int, data: dict, db: DB):
    result = await db.execute(select(Question).where(Question.id == question_id))
    question = result.scalar_one_or_none()
    if not question:
        raise HTTPException(status_code=404, detail="Question not found")
    
    for field, value in data.items():
        if field != "answers" and hasattr(question, field):
            setattr(question, field, value)
    
    await db.commit()
    await db.refresh(question)
    return {
        "id": question.id,
        "level_id": question.level_id,
        "question_text": question.question_text,
        "question_type": question.question_type,
        "difficulty": question.difficulty,
        "sequence_order": question.sequence_order,
        "time_limit_seconds": question.time_limit_seconds,
        "points": question.points,
        "hint_text": question.hint_text,
        "explanation": question.explanation,
        "media_url": question.media_url,
        "is_verified": question.is_verified,
        "is_active": question.is_active,
    }


@router.delete("/questions/{question_id}", summary="Delete Question")
async def delete_question(question_id: int, db: DB):
    result = await db.execute(select(Question).where(Question.id == question_id))
    question = result.scalar_one_or_none()
    if not question:
        raise HTTPException(status_code=404, detail="Question not found")
    
    question.is_active = False
    await db.commit()
    return {"message": "Question deactivated"}


# ===================== TEACHER QUESTION REVIEWS =====================

@router.get("/question-submissions", summary="List Teacher Question Submissions")
async def list_submissions(db: DB, status: Optional[str] = None):
    from app.models.academic import TeacherQuestionSubmission
    query = select(TeacherQuestionSubmission)
    if status:
        query = query.where(TeacherQuestionSubmission.status == status)
    result = await db.execute(query.order_by(TeacherQuestionSubmission.created_at.desc()))
    return result.scalars().all()


@router.post("/question-submissions/{submission_id}/review", summary="Review Teacher Submission")
async def review_submission(
    submission_id: int,
    data: dict,
    db: DB,
    current_user: AdminUser,
):
    from app.models.academic import TeacherQuestionSubmission, Question, AnswerOption
    from app.schemas.academic import ReviewSubmissionRequest
    from datetime import datetime, timezone

    result = await db.execute(
        select(TeacherQuestionSubmission).where(TeacherQuestionSubmission.id == submission_id)
    )
    submission = result.scalar_one_or_none()
    if not submission:
        raise HTTPException(status_code=404, detail="Submission not found")

    submission.status = data.get("status", "pending")
    submission.review_comments = data.get("review_comments")
    submission.reviewed_by_user_id = current_user.id
    submission.reviewed_at = datetime.now(timezone.utc)

    if submission.status == "approved":
        # Create actual question from submission
        question = Question(
            level_id=submission.topic_level_id,
            question_text=submission.question_text,
            question_type=submission.question_type,
            difficulty=submission.difficulty,
            hint_text=submission.hint_text,
            explanation=submission.explanation,
            submitted_by_teacher_id=submission.teacher_user_id,
            is_verified=True,
            verified_by_user_id=current_user.id,
        )
        db.add(question)
        await db.flush()

        if submission.answers_json:
            for i, ans in enumerate(submission.answers_json):
                answer = AnswerOption(
                    question_id=question.id,
                    answer_text=ans.get("text", ""),
                    is_correct=ans.get("is_correct", False),
                    explanation=ans.get("explanation"),
                    display_order=i + 1,
                )
                db.add(answer)

        submission.approved_question_id = question.id

    await db.commit()
    return {"message": f"Submission {submission.status}", "submission_id": submission_id}


# ===================== PACKAGES & SUBSCRIPTIONS =====================

@router.get("/packages", summary="List Packages")
async def list_packages(db: DB):
    result = await db.execute(select(Package).order_by(Package.name))
    packages = result.scalars().all()
    return [
        {
            "id": p.id, "name": p.name, "slug": p.slug, "display_name": p.display_name,
            "description": p.description, "price": p.price, "currency": p.currency,
            "duration_days": p.duration_days, "is_offline_capable": p.is_offline_capable,
            "requires_subscription_check": p.requires_subscription_check,
            "grace_period_days": p.grace_period_days, "features": p.features,
            "is_active": p.is_active,
        }
        for p in packages
    ]


@router.post("/packages", summary="Create Package", status_code=201)
async def create_package(data: dict, db: DB):
    name = data.get("name", "freemium")
    if name not in ("freemium", "premium"):
        raise HTTPException(status_code=400, detail="name must be 'freemium' or 'premium'")
    if not data.get("slug") or not data.get("display_name"):
        raise HTTPException(status_code=400, detail="slug and display_name are required")
    pkg = Package(
        name=name,
        slug=data["slug"],
        display_name=data["display_name"],
        description=data.get("description"),
        price=data.get("price", 0),
        currency=data.get("currency", "UGX"),
        duration_days=data.get("duration_days") or None,
        is_offline_capable=data.get("is_offline_capable", False),
        requires_subscription_check=data.get("requires_subscription_check", False),
        grace_period_days=data.get("grace_period_days", 3),
        features=data.get("features"),
        is_active=True,
    )
    db.add(pkg)
    await db.commit()
    await db.refresh(pkg)
    return {
        "id": pkg.id, "name": pkg.name, "slug": pkg.slug, "display_name": pkg.display_name,
        "description": pkg.description, "price": pkg.price, "currency": pkg.currency,
        "duration_days": pkg.duration_days, "is_offline_capable": pkg.is_offline_capable,
        "requires_subscription_check": pkg.requires_subscription_check,
        "grace_period_days": pkg.grace_period_days, "features": pkg.features,
        "is_active": pkg.is_active,
    }


@router.patch("/packages/{package_id}", summary="Update Package")
async def update_package(package_id: int, data: dict, db: DB):
    result = await db.execute(select(Package).where(Package.id == package_id))
    pkg = result.scalar_one_or_none()
    if not pkg:
        raise HTTPException(status_code=404, detail="Package not found")
    allowed = {"slug", "display_name", "description", "price", "currency", "duration_days",
               "is_offline_capable", "requires_subscription_check", "grace_period_days",
               "features", "is_active"}
    for field, value in data.items():
        if field in allowed:
            setattr(pkg, field, value if value != "" else None)
    await db.commit()
    await db.refresh(pkg)
    return {
        "id": pkg.id, "name": pkg.name, "slug": pkg.slug, "display_name": pkg.display_name,
        "description": pkg.description, "price": pkg.price, "currency": pkg.currency,
        "duration_days": pkg.duration_days, "is_offline_capable": pkg.is_offline_capable,
        "requires_subscription_check": pkg.requires_subscription_check,
        "grace_period_days": pkg.grace_period_days, "features": pkg.features,
        "is_active": pkg.is_active,
    }


@router.delete("/packages/{package_id}", summary="Deactivate Package")
async def delete_package(package_id: int, db: DB):
    result = await db.execute(select(Package).where(Package.id == package_id))
    pkg = result.scalar_one_or_none()
    if not pkg:
        raise HTTPException(status_code=404, detail="Package not found")
    pkg.is_active = False
    await db.commit()
    return {"message": "Package deactivated"}


@router.get("/subscriptions", summary="List Subscriptions")
async def list_subscriptions(db: DB, status: Optional[str] = None):
    query = select(Subscription)
    if status:
        query = query.where(Subscription.status == status)
    result = await db.execute(query.order_by(Subscription.created_at.desc()).limit(100))
    return result.scalars().all()


@router.get("/payments", summary="List Payments")
async def list_payments(db: DB, status: Optional[str] = None):
    query = select(Payment)
    if status:
        query = query.where(Payment.status == status)
    result = await db.execute(query.order_by(Payment.created_at.desc()).limit(100))
    return result.scalars().all()


# ===================== AUDIT LOGS =====================

@router.get("/audit-logs", summary="View Audit Logs")
async def list_audit_logs(
    db: DB,
    page: int = Query(1, ge=1),
    page_size: int = Query(50, ge=1, le=200),
):
    offset = (page - 1) * page_size
    result = await db.execute(
        select(AuditLog).order_by(AuditLog.created_at.desc()).offset(offset).limit(page_size)
    )
    return result.scalars().all()


# ===================== GAME TOPICS =====================

def _fmt_game_topic(t: GameTopic) -> dict:
    return {
        "id": t.id,
        "game_id": t.game_id,
        "subject_id": t.subject_id,
        "subject_name": t.subject.name if t.subject else None,
        "name": t.name,
        "slug": t.slug,
        "description": t.description,
        "location_name": t.location_name,
        "icon_url": t.icon_url,
        "background_url": t.background_url,
        "color_hex": t.color_hex,
        "sequence_order": t.sequence_order,
        "is_active": t.is_active,
        "created_at": t.created_at.isoformat() if t.created_at else None,
    }


@router.get("/games/{game_id}/topics", summary="List Game Topics")
async def list_game_topics(game_id: int, db: DB):
    """Get all topics (locations/modules) for a game, ordered by sequence."""
    from sqlalchemy.orm import selectinload
    result = await db.execute(
        select(GameTopic)
        .options(selectinload(GameTopic.subject))
        .where(GameTopic.game_id == game_id, GameTopic.is_active == True)
        .order_by(GameTopic.sequence_order)
    )
    topics = result.scalars().all()
    return [_fmt_game_topic(t) for t in topics]


@router.post("/games/{game_id}/topics", summary="Create Game Topic", status_code=201)
async def create_game_topic(game_id: int, data: dict, db: DB):
    """Create a new topic/location within a game."""
    # Validate game exists
    g_result = await db.execute(select(Game).where(Game.id == game_id))
    if not g_result.scalar_one_or_none():
        raise HTTPException(status_code=404, detail="Game not found")

    # Check slug uniqueness within game
    existing = await db.execute(
        select(GameTopic).where(GameTopic.game_id == game_id, GameTopic.slug == data.get("slug", ""))
    )
    if existing.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="A topic with this slug already exists in this game")

    topic = GameTopic(
        game_id=game_id,
        subject_id=data.get("subject_id") or None,
        name=data.get("name"),
        slug=data.get("slug"),
        description=data.get("description"),
        location_name=data.get("location_name"),
        icon_url=data.get("icon_url"),
        background_url=data.get("background_url"),
        color_hex=data.get("color_hex", "#4F46E5"),
        sequence_order=data.get("sequence_order", 1),
        is_active=True,
    )
    db.add(topic)
    await db.commit()
    # Reload with subject relationship
    from sqlalchemy.orm import selectinload
    res2 = await db.execute(
        select(GameTopic).options(selectinload(GameTopic.subject)).where(GameTopic.id == topic.id)
    )
    topic = res2.scalar_one()
    return _fmt_game_topic(topic)


@router.patch("/game-topics/{topic_id}", summary="Update Game Topic")
async def update_game_topic(topic_id: int, data: dict, db: DB):
    result = await db.execute(select(GameTopic).where(GameTopic.id == topic_id))
    topic = result.scalar_one_or_none()
    if not topic:
        raise HTTPException(status_code=404, detail="Game topic not found")

    allowed = {"name", "slug", "description", "location_name", "icon_url",
               "background_url", "color_hex", "sequence_order", "is_active", "subject_id"}
    for field, value in data.items():
        if field in allowed:
            setattr(topic, field, value if value != 0 else None if field == "subject_id" else value)

    await db.commit()
    # Reload with subject relationship
    from sqlalchemy.orm import selectinload
    res2 = await db.execute(
        select(GameTopic).options(selectinload(GameTopic.subject)).where(GameTopic.id == topic_id)
    )
    topic = res2.scalar_one()
    return _fmt_game_topic(topic)


@router.delete("/game-topics/{topic_id}", summary="Delete Game Topic")
async def delete_game_topic(topic_id: int, db: DB):
    result = await db.execute(select(GameTopic).where(GameTopic.id == topic_id))
    topic = result.scalar_one_or_none()
    if not topic:
        raise HTTPException(status_code=404, detail="Game topic not found")
    topic.is_active = False
    await db.commit()
    return {"message": "Game topic deactivated"}


@router.get("/game-topics/{topic_id}/levels", summary="Get Levels under a Game Topic")
async def get_topic_levels_for_game_topic(topic_id: int, db: DB):
    """List all game levels under a specific game topic."""
    result = await db.execute(
        select(GameLevelModel)
        .where(GameLevelModel.game_topic_id == topic_id, GameLevelModel.is_active == True)
        .order_by(GameLevelModel.sequence_order)
    )
    levels = result.scalars().all()
    return [
        {
            "id": l.id,
            "game_id": l.game_id,
            "game_topic_id": l.game_topic_id,
            "topic_level_id": l.topic_level_id,
            "title": l.title,
            "description": l.description,
            "instructions": l.instructions,
            "tag": l.tag,
            "sequence_order": l.sequence_order,
            "is_active": l.is_active,
        }
        for l in levels
    ]


@router.post("/game-topics/{topic_id}/levels", summary="Create Level under Game Topic", status_code=201)
async def create_level_under_topic(topic_id: int, data: dict, db: DB):
    """
    Create a TopicLevel + GameLevel under a GameTopic in one call.
    Required: title, name (topic level name).
    Optional: topic_id (academic topic link), level_number, xp_reward,
              passing_score, time_limit_seconds, stars_max, instructions,
              description, tag, sequence_order.
    """
    # Validate game topic
    t_result = await db.execute(select(GameTopic).where(GameTopic.id == topic_id))
    game_topic = t_result.scalar_one_or_none()
    if not game_topic:
        raise HTTPException(status_code=404, detail="Game topic not found")

    # Resolve academic link: existing TopicLevel takes priority over creating a new one
    direct_tl_id = data.get("topic_level_id")  # link to existing TopicLevel
    academic_topic_id = data.get("academic_topic_id")  # create new TopicLevel under this topic
    topic_level_id = None

    if direct_tl_id:
        # Validate the TopicLevel exists
        tl_res = await db.execute(select(TopicLevel).where(TopicLevel.id == direct_tl_id))
        if not tl_res.scalar_one_or_none():
            raise HTTPException(status_code=404, detail="TopicLevel not found")
        topic_level_id = direct_tl_id
    elif academic_topic_id:
        # Auto-determine next available level_number to avoid unique constraint (topic_id, level_number)
        requested_num = data.get("level_number", 1)
        max_res = await db.execute(
            select(func.max(TopicLevel.level_number)).where(TopicLevel.topic_id == academic_topic_id)
        )
        current_max = max_res.scalar() or 0
        level_number = max(requested_num, current_max + 1)

        topic_level = TopicLevel(
            topic_id=academic_topic_id,
            level_number=level_number,
            name=data.get("name") or data.get("title", "Level"),
            description=data.get("description"),
            instructions=data.get("instructions"),
            xp_reward=data.get("xp_reward", 10),
            passing_score=data.get("passing_score", 70),
            time_limit_seconds=data.get("time_limit_seconds", 60),
            stars_max=data.get("stars_max", 3),
            is_active=True,
        )
        db.add(topic_level)
        try:
            await db.flush()
        except IntegrityError:
            await db.rollback()
            raise HTTPException(
                status_code=409,
                detail=f"A TopicLevel with number {level_number} already exists for this topic."
            )
        topic_level_id = topic_level.id

    # Create game level
    game_level = GameLevelModel(
        game_id=game_topic.game_id,
        game_topic_id=topic_id,
        topic_level_id=topic_level_id,
        title=data.get("title") or data.get("name", "Level"),
        description=data.get("description"),
        instructions=data.get("instructions"),
        tag=data.get("tag"),
        sequence_order=data.get("sequence_order", 1),
        is_active=True,
    )
    db.add(game_level)
    try:
        await db.commit()
    except IntegrityError:
        await db.rollback()
        raise HTTPException(status_code=409, detail="A game level with this sequence order already exists.")
    await db.refresh(game_level)

    return {
        "id": game_level.id,
        "game_id": game_level.game_id,
        "game_topic_id": game_level.game_topic_id,
        "topic_level_id": game_level.topic_level_id,
        "title": game_level.title,
        "description": game_level.description,
        "instructions": game_level.instructions,
        "tag": game_level.tag,
        "sequence_order": game_level.sequence_order,
        "is_active": game_level.is_active,
    }


# ===================== GAME CONTENT HIERARCHY =====================

def _fmt_question(q: Question) -> dict:
    return {
        "id": q.id,
        "level_id": q.level_id,
        "question_text": q.question_text,
        "question_type": q.question_type,
        "difficulty": q.difficulty,
        "sequence_order": q.sequence_order,
        "time_limit_seconds": q.time_limit_seconds,
        "points": q.points,
        "hint_text": q.hint_text,
        "explanation": q.explanation,
        "media_url": q.media_url,
        "tag": q.tag,
        "is_verified": q.is_verified,
        "is_active": q.is_active,
    }


def _fmt_answer(a: AnswerOption) -> dict:
    return {
        "id": a.id,
        "question_id": a.question_id,
        "answer_text": a.answer_text,
        "is_correct": a.is_correct,
        "display_order": a.display_order,
        "explanation": a.explanation,
        "asset_2d_id": a.asset_2d_id,
        "asset_3d_id": a.asset_3d_id,
        "asset_vr_id": a.asset_vr_id,
        "asset_group": a.asset_group,
        "monetary_value": a.monetary_value,
        "quantity": a.quantity,
    }


# ---- Single game detail ----

@router.get("/games/{game_id}", summary="Get Single Game")
async def get_game(game_id: int, db: DB):
    result = await db.execute(select(Game).where(Game.id == game_id))
    game = result.scalar_one_or_none()
    if not game:
        raise HTTPException(status_code=404, detail="Game not found")
    return {
        "id": game.id,
        "name": game.name,
        "slug": game.slug,
        "description": game.description,
        "instructions": game.instructions,
        "intro_video_url": game.intro_video_url,
        "artwork_url": game.artwork_url,
        "game_type_id": game.game_type_id,
        "subject_grade_id": game.subject_grade_id,
        "is_active": game.is_active,
        "created_at": game.created_at.isoformat() if game.created_at else None,
    }


# ---- Topic Levels ----

@router.get("/topic-levels", summary="List Topic Levels")
async def list_topic_levels(db: DB, topic_id: Optional[int] = None):
    """List all topic levels with their parent topic/subject/grade info."""
    query = (
        select(TopicLevel, Topic, SubjectGrade, Subject, Grade)
        .join(Topic, TopicLevel.topic_id == Topic.id)
        .join(SubjectGrade, Topic.subject_grade_id == SubjectGrade.id)
        .join(Subject, SubjectGrade.subject_id == Subject.id)
        .join(Grade, SubjectGrade.grade_id == Grade.id)
    )
    if topic_id:
        query = query.where(TopicLevel.topic_id == topic_id)
    query = query.order_by(Topic.id, TopicLevel.level_number)
    result = await db.execute(query)
    rows = result.all()
    return [
        {
            "id": row.TopicLevel.id,
            "topic_id": row.TopicLevel.topic_id,
            "topic_name": row.Topic.name,
            "subject_name": row.Subject.name,
            "grade_name": row.Grade.name,
            "level_number": row.TopicLevel.level_number,
            "name": row.TopicLevel.name,
            "description": row.TopicLevel.description,
            "instructions": row.TopicLevel.instructions,
            "xp_reward": row.TopicLevel.xp_reward,
            "passing_score": row.TopicLevel.passing_score,
            "time_limit_seconds": row.TopicLevel.time_limit_seconds,
            "stars_max": row.TopicLevel.stars_max,
            "is_active": row.TopicLevel.is_active,
        }
        for row in rows
    ]


@router.get("/topic-levels/{level_id}", summary="Get Topic Level with Questions and Answers")
async def get_topic_level_detail(level_id: int, db: DB):
    """Get a topic level with all its questions and answer options."""
    result = await db.execute(select(TopicLevel).where(TopicLevel.id == level_id))
    level = result.scalar_one_or_none()
    if not level:
        raise HTTPException(status_code=404, detail="Topic level not found")

    # Get questions
    q_result = await db.execute(
        select(Question)
        .where(Question.level_id == level_id, Question.is_active == True)
        .order_by(Question.sequence_order)
    )
    questions = q_result.scalars().all()

    questions_out = []
    for q in questions:
        a_result = await db.execute(
            select(AnswerOption)
            .where(AnswerOption.question_id == q.id)
            .order_by(AnswerOption.display_order)
        )
        answers = a_result.scalars().all()
        q_dict = _fmt_question(q)
        q_dict["answers"] = [_fmt_answer(a) for a in answers]
        questions_out.append(q_dict)

    return {
        "id": level.id,
        "topic_id": level.topic_id,
        "level_number": level.level_number,
        "name": level.name,
        "description": level.description,
        "instructions": level.instructions,
        "xp_reward": level.xp_reward,
        "passing_score": level.passing_score,
        "time_limit_seconds": level.time_limit_seconds,
        "stars_max": level.stars_max,
        "is_active": level.is_active,
        "questions": questions_out,
    }


@router.patch("/topic-levels/{level_id}", summary="Update Topic Level")
async def update_topic_level(level_id: int, data: dict, db: DB):
    result = await db.execute(select(TopicLevel).where(TopicLevel.id == level_id))
    level = result.scalar_one_or_none()
    if not level:
        raise HTTPException(status_code=404, detail="Topic level not found")
    allowed = {"name", "description", "instructions", "level_number", "xp_reward",
               "passing_score", "time_limit_seconds", "stars_max", "thumbnail_url", "is_active"}
    for field, value in data.items():
        if field in allowed:
            setattr(level, field, value)
    await db.commit()
    await db.refresh(level)
    return {
        "id": level.id,
        "topic_id": level.topic_id,
        "level_number": level.level_number,
        "name": level.name,
        "description": level.description,
        "instructions": level.instructions,
        "xp_reward": level.xp_reward,
        "passing_score": level.passing_score,
        "time_limit_seconds": level.time_limit_seconds,
        "stars_max": level.stars_max,
        "is_active": level.is_active,
    }


# ---- Combined: create TopicLevel + GameLevel in one call ----

@router.post("/games/{game_id}/full-level", summary="Create Topic Level + Game Level", status_code=201)
async def create_full_level(game_id: int, data: dict, db: DB):
    """
    Creates a new TopicLevel and a GameLevel linking it to the game.
    Body fields: topic_id, level_number, name, description, instructions,
                 xp_reward, passing_score, time_limit_seconds, stars_max,
                 game_level_title, game_level_sequence_order, game_level_tag
    """
    # Validate game exists
    g_result = await db.execute(select(Game).where(Game.id == game_id))
    if not g_result.scalar_one_or_none():
        raise HTTPException(status_code=404, detail="Game not found")

    # Create TopicLevel
    topic_level = TopicLevel(
        topic_id=data["topic_id"],
        level_number=data.get("level_number", 1),
        name=data.get("name", "Level"),
        description=data.get("description"),
        instructions=data.get("instructions"),
        xp_reward=data.get("xp_reward", 10),
        passing_score=data.get("passing_score", 70),
        time_limit_seconds=data.get("time_limit_seconds", 300),
        stars_max=data.get("stars_max", 3),
        thumbnail_url=data.get("thumbnail_url"),
        is_active=True,
    )
    db.add(topic_level)
    await db.flush()

    # Create GameLevel
    game_level = GameLevelModel(
        game_id=game_id,
        topic_level_id=topic_level.id,
        title=data.get("game_level_title") or data.get("name", "Level"),
        description=data.get("description"),
        instructions=data.get("instructions"),
        tag=data.get("game_level_tag"),
        sequence_order=data.get("game_level_sequence_order", 1),
        is_active=True,
    )
    db.add(game_level)
    await db.commit()
    await db.refresh(game_level)
    await db.refresh(topic_level)

    return {
        "game_level_id": game_level.id,
        "topic_level_id": topic_level.id,
        "game_id": game_level.game_id,
        "topic_id": topic_level.topic_id,
        "title": game_level.title,
        "name": topic_level.name,
        "level_number": topic_level.level_number,
        "xp_reward": topic_level.xp_reward,
        "passing_score": topic_level.passing_score,
        "time_limit_seconds": topic_level.time_limit_seconds,
        "stars_max": topic_level.stars_max,
        "sequence_order": game_level.sequence_order,
        "is_active": game_level.is_active,
    }


# ---- Questions with full answers ----

@router.get("/questions/{question_id}", summary="Get Question with Answers")
async def get_question_detail(question_id: int, db: DB):
    result = await db.execute(select(Question).where(Question.id == question_id))
    question = result.scalar_one_or_none()
    if not question:
        raise HTTPException(status_code=404, detail="Question not found")
    a_result = await db.execute(
        select(AnswerOption)
        .where(AnswerOption.question_id == question_id)
        .order_by(AnswerOption.display_order)
    )
    answers = a_result.scalars().all()
    q_dict = _fmt_question(question)
    q_dict["answers"] = [_fmt_answer(a) for a in answers]
    return q_dict


# ---- Answer CRUD ----

@router.get("/questions/{question_id}/answers", summary="List Answers for Question")
async def list_answers(question_id: int, db: DB):
    result = await db.execute(
        select(AnswerOption)
        .where(AnswerOption.question_id == question_id)
        .order_by(AnswerOption.display_order)
    )
    answers = result.scalars().all()
    return [_fmt_answer(a) for a in answers]


@router.post("/questions/{question_id}/answers", summary="Add Answer to Question", status_code=201)
async def add_answer(question_id: int, data: dict, db: DB):
    # Verify question exists
    q_result = await db.execute(select(Question).where(Question.id == question_id))
    if not q_result.scalar_one_or_none():
        raise HTTPException(status_code=404, detail="Question not found")

    # Auto display_order
    count_result = await db.execute(
        select(func.count(AnswerOption.id)).where(AnswerOption.question_id == question_id)
    )
    next_order = (count_result.scalar() or 0) + 1

    answer = AnswerOption(
        question_id=question_id,
        answer_text=data.get("answer_text", ""),
        is_correct=data.get("is_correct", False),
        display_order=data.get("display_order", next_order),
        explanation=data.get("explanation"),
        asset_2d_id=data.get("asset_2d_id"),
        asset_3d_id=data.get("asset_3d_id"),
        asset_vr_id=data.get("asset_vr_id"),
        asset_group=data.get("asset_group"),
        monetary_value=data.get("monetary_value"),
        quantity=data.get("quantity", 1),
    )
    db.add(answer)
    await db.commit()
    await db.refresh(answer)
    return _fmt_answer(answer)


@router.patch("/answers/{answer_id}", summary="Update Answer")
async def update_answer(answer_id: int, data: dict, db: DB):
    result = await db.execute(select(AnswerOption).where(AnswerOption.id == answer_id))
    answer = result.scalar_one_or_none()
    if not answer:
        raise HTTPException(status_code=404, detail="Answer not found")
    allowed = {"answer_text", "is_correct", "display_order", "explanation",
               "asset_2d_id", "asset_3d_id", "asset_vr_id", "asset_group",
               "monetary_value", "quantity"}
    for field, value in data.items():
        if field in allowed:
            setattr(answer, field, value)
    await db.commit()
    await db.refresh(answer)
    return _fmt_answer(answer)


@router.delete("/answers/{answer_id}", summary="Delete Answer")
async def delete_answer(answer_id: int, db: DB):
    result = await db.execute(select(AnswerOption).where(AnswerOption.id == answer_id))
    answer = result.scalar_one_or_none()
    if not answer:
        raise HTTPException(status_code=404, detail="Answer not found")
    await db.delete(answer)
    await db.commit()
    return {"message": "Answer deleted"}

