"""
TimetableGen SaaS — Multi-tenant FastAPI Application
Each school has isolated data. Login required.
"""
import sys, os
sys.path.insert(0, os.path.dirname(__file__))

from fastapi import FastAPI, Request, Form, HTTPException, Depends
from fastapi.responses import HTMLResponse, JSONResponse, Response, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from typing import Optional

from database import get_db, init_db
from auth import (
    get_current_user, require_auth, login_user,
    create_session, delete_session, register_school,
    invite_user, SESSION_COOKIE, hash_password, verify_password
)
from services.generator import generate_timetable, detect_clashes, get_substitute_suggestions
from services.pdf_export import export_class_timetable_pdf, export_teacher_timetable_pdf

app = FastAPI(title="TimetableGen SaaS", version="2.0.0")
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")

DAYS_ORDER = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]

@app.on_event("startup")
def startup():
    init_db()

# ── Helpers ──────────────────────────────────────────────────────────────────
def _days(cfg_days: str) -> list[str]:
    return [d for d in DAYS_ORDER if d in cfg_days.split(",")]

def _active_cfg(db, school_id: int):
    return db.execute(
        "SELECT * FROM timetable_config WHERE school_id=? AND is_active=1 ORDER BY id DESC LIMIT 1",
        (school_id,)
    ).fetchone()

def _render(request, template, ctx={}):
    user = get_current_user(request)
    ctx.update({"user": user})
    return templates.TemplateResponse(request, template, ctx)

def _redirect_login():
    return RedirectResponse("/login", status_code=302)

# ═════════════════════════════════════════════════════════════════════════════
# PUBLIC ROUTES — no login needed
# ═════════════════════════════════════════════════════════════════════════════

@app.get("/", response_class=HTMLResponse)
async def landing(request: Request):
    user = get_current_user(request)
    if user:
        return RedirectResponse("/dashboard", status_code=302)
    db = get_db()
    school_count = db.execute("SELECT COUNT(*) FROM school").fetchone()[0]
    db.close()
    return templates.TemplateResponse(request, "landing.html", {"school_count": school_count})

# ── Register ──────────────────────────────────────────────────────────────────
@app.get("/register", response_class=HTMLResponse)
async def register_page(request: Request):
    if get_current_user(request):
        return RedirectResponse("/dashboard", status_code=302)
    return templates.TemplateResponse(request, "register.html", {"error": None})

@app.post("/register", response_class=HTMLResponse)
async def do_register(
    request: Request,
    school_name:    str = Form(...),
    board:          str = Form("CBSE"),
    city:           str = Form(""),
    state:          str = Form(""),
    admin_name:     str = Form(...),
    admin_email:    str = Form(...),
    admin_password: str = Form(...),
    confirm_password: str = Form(...),
):
    if admin_password != confirm_password:
        return templates.TemplateResponse(request, "register.html",
            {"error": "Passwords do not match.", "form": dict(await request.form())})
    if len(admin_password) < 8:
        return templates.TemplateResponse(request, "register.html",
            {"error": "Password must be at least 8 characters.", "form": dict(await request.form())})

    ok, msg, school_id = register_school(
        school_name, board, city, state,
        admin_name, admin_email, admin_password
    )
    if not ok:
        return templates.TemplateResponse(request, "register.html",
            {"error": msg, "form": dict(await request.form())})

    # Auto-login after register
    db = get_db()
    user = db.execute("SELECT * FROM user WHERE email=?", (admin_email.lower(),)).fetchone()
    db.close()
    token = create_session(user["id"], school_id)
    response = RedirectResponse("/dashboard?welcome=1", status_code=302)
    response.set_cookie(SESSION_COOKIE, token, max_age=86400*30,
                        httponly=True, samesite="lax")
    return response

# ── Login ─────────────────────────────────────────────────────────────────────
@app.get("/login", response_class=HTMLResponse)
async def login_page(request: Request):
    if get_current_user(request):
        return RedirectResponse("/dashboard", status_code=302)
    return templates.TemplateResponse(request, "login.html", {"error": None})

@app.post("/login", response_class=HTMLResponse)
async def do_login(
    request: Request,
    email:    str = Form(...),
    password: str = Form(...),
):
    ok, msg, user = login_user(email, password)
    if not ok:
        return templates.TemplateResponse(request, "login.html", {"error": msg})
    token = create_session(user["id"], user["school_id"])
    response = RedirectResponse("/dashboard", status_code=302)
    response.set_cookie(SESSION_COOKIE, token, max_age=86400*30,
                        httponly=True, samesite="lax")
    return response

@app.get("/logout")
async def logout(request: Request):
    token = request.cookies.get(SESSION_COOKIE)
    if token:
        delete_session(token)
    response = RedirectResponse("/login", status_code=302)
    response.delete_cookie(SESSION_COOKIE)
    return response

# ═════════════════════════════════════════════════════════════════════════════
# PROTECTED ROUTES — login required
# ═════════════════════════════════════════════════════════════════════════════

@app.get("/dashboard", response_class=HTMLResponse)
async def dashboard(request: Request):
    user = get_current_user(request)
    if not user:
        return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    school = db.execute("SELECT * FROM school WHERE id=?", (sid,)).fetchone()
    cfg    = _active_cfg(db, sid)
    stats  = {
        "classes":  db.execute("SELECT COUNT(*) FROM class_section WHERE school_id=?", (sid,)).fetchone()[0],
        "teachers": db.execute("SELECT COUNT(*) FROM teacher WHERE school_id=? AND is_active=1", (sid,)).fetchone()[0],
        "subjects": db.execute("SELECT COUNT(*) FROM subject WHERE school_id=?", (sid,)).fetchone()[0],
        "has_timetable": bool(cfg and db.execute(
            "SELECT 1 FROM timetable WHERE config_id=?", (cfg["id"],)).fetchone()),
    }
    clashes = detect_clashes(cfg["id"]) if cfg and stats["has_timetable"] else []
    welcome = request.query_params.get("welcome") == "1"
    db.close()
    return templates.TemplateResponse(request, "dashboard.html", {
        "user": user, "school": school, "cfg": cfg,
        "stats": stats, "clashes": clashes, "welcome": welcome,
    })

# ── School Setup ──────────────────────────────────────────────────────────────
@app.get("/setup", response_class=HTMLResponse)
async def setup_page(request: Request):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    school = db.execute("SELECT * FROM school WHERE id=?", (sid,)).fetchone()
    cfg    = db.execute("SELECT * FROM timetable_config WHERE school_id=? ORDER BY id DESC LIMIT 1", (sid,)).fetchone()
    db.close()
    return templates.TemplateResponse(request, "setup.html", {
        "user": user, "school": school, "cfg": cfg
    })

@app.post("/setup/school")
async def save_school(
    request: Request,
    name: str = Form(...), address: str = Form(""),
    board: str = Form("CBSE"), city: str = Form(""),
    state: str = Form(""), phone: str = Form(""), email: str = Form(""),
):
    user = get_current_user(request)
    if not user: return _redirect_login()
    if user["role"] not in ("owner", "admin"):
        return HTMLResponse('<div class="toast error">✗ Permission denied</div>')
    db = get_db()
    db.execute(
        "UPDATE school SET name=?,address=?,board=?,city=?,state=?,phone=?,email=? WHERE id=?",
        (name, address, board, city, state, phone, email, user["school_id"])
    )
    db.commit(); db.close()
    return HTMLResponse('<div class="toast success">✓ School info saved!</div>')

@app.post("/setup/config")
async def save_config(
    request: Request,
    academic_year: str = Form("2025-26"),
    working_days: str = Form(...),
    periods_per_day: int = Form(8),
    period_duration: int = Form(45),
    break_after: str = Form("4"),
    break_duration: int = Form(30),
    lab_days: str = Form("Wed,Thu"),
    pt_days: str = Form("Sat"),
):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    existing = db.execute(
        "SELECT id FROM timetable_config WHERE school_id=? ORDER BY id DESC LIMIT 1", (sid,)
    ).fetchone()
    if existing:
        db.execute(
            """UPDATE timetable_config SET academic_year=?,working_days=?,
               periods_per_day=?,period_duration=?,break_after=?,break_duration=?,
               lab_days=?,pt_days=? WHERE id=?""",
            (academic_year, working_days, periods_per_day, period_duration,
             break_after, break_duration, lab_days, pt_days, existing["id"])
        )
    else:
        db.execute(
            """INSERT INTO timetable_config
               (school_id,academic_year,working_days,periods_per_day,period_duration,
               break_after,break_duration,lab_days,pt_days)
               VALUES (?,?,?,?,?,?,?,?,?)""",
            (sid, academic_year, working_days, periods_per_day, period_duration,
             break_after, break_duration, lab_days, pt_days)
        )
    db.commit(); db.close()
    return HTMLResponse('<div class="toast success">✓ Configuration saved!</div>')

# ── Classes ────────────────────────────────────────────────────────────────────
@app.get("/classes", response_class=HTMLResponse)
async def classes_page(request: Request):
    user = get_current_user(request)
    if not user: return _redirect_login()
    db = get_db()
    classes = db.execute(
        "SELECT * FROM class_section WHERE school_id=? ORDER BY grade,section",
        (user["school_id"],)
    ).fetchall()
    db.close()
    return templates.TemplateResponse(request, "classes.html", {"user": user, "classes": classes})

@app.post("/classes/add")
async def add_class(
    request: Request,
    grade: str = Form(...), section: str = Form("A"), room_no: str = Form("")
):
    user = get_current_user(request)
    if not user: return _redirect_login()
    db = get_db()
    try:
        db.execute(
            "INSERT INTO class_section (school_id,grade,section,room_no) VALUES (?,?,?,?)",
            (user["school_id"], grade.upper(), section.upper(), room_no)
        )
        db.commit()
    except Exception as e:
        db.close()
        return HTMLResponse(f'<div class="toast error">✗ {e}</div>')
    classes = db.execute(
        "SELECT * FROM class_section WHERE school_id=? ORDER BY grade,section",
        (user["school_id"],)
    ).fetchall()
    db.close()
    return templates.TemplateResponse(request, "partials/class_list.html", {"classes": classes, "user": user})

@app.delete("/classes/{class_id}")
async def delete_class(request: Request, class_id: int):
    user = get_current_user(request)
    if not user: return _redirect_login()
    db = get_db()
    db.execute("DELETE FROM class_section WHERE id=? AND school_id=?", (class_id, user["school_id"]))
    db.commit()
    classes = db.execute(
        "SELECT * FROM class_section WHERE school_id=? ORDER BY grade,section",
        (user["school_id"],)
    ).fetchall()
    db.close()
    return templates.TemplateResponse(request, "partials/class_list.html", {"classes": classes, "user": user})

# ── Subjects ───────────────────────────────────────────────────────────────────
@app.get("/subjects", response_class=HTMLResponse)
async def subjects_page(request: Request):
    user = get_current_user(request)
    if not user: return _redirect_login()
    db = get_db()
    subjects = db.execute("SELECT * FROM subject WHERE school_id=? ORDER BY name", (user["school_id"],)).fetchall()
    db.close()
    return templates.TemplateResponse(request, "subjects.html", {"user": user, "subjects": subjects})

@app.post("/subjects/add")
async def add_subject(
    request: Request,
    name: str = Form(...), code: str = Form(...),
    subject_type: str = Form("theory"), color: str = Form("#4F8EF7")
):
    user = get_current_user(request)
    if not user: return _redirect_login()
    db = get_db()
    try:
        db.execute(
            "INSERT INTO subject (school_id,name,code,subject_type,color) VALUES (?,?,?,?,?)",
            (user["school_id"], name, code.upper(), subject_type, color)
        )
        db.commit()
    except Exception as e:
        db.close()
        return HTMLResponse(f'<div class="toast error">✗ {e}</div>')
    subjects = db.execute("SELECT * FROM subject WHERE school_id=? ORDER BY name", (user["school_id"],)).fetchall()
    db.close()
    return templates.TemplateResponse(request, "partials/subject_list.html", {"subjects": subjects, "user": user})

@app.delete("/subjects/{subject_id}")
async def delete_subject(request: Request, subject_id: int):
    user = get_current_user(request)
    if not user: return _redirect_login()
    db = get_db()
    db.execute("DELETE FROM subject WHERE id=? AND school_id=?", (subject_id, user["school_id"]))
    db.commit()
    subjects = db.execute("SELECT * FROM subject WHERE school_id=? ORDER BY name", (user["school_id"],)).fetchall()
    db.close()
    return templates.TemplateResponse(request, "partials/subject_list.html", {"subjects": subjects, "user": user})

# ── Teachers ───────────────────────────────────────────────────────────────────
@app.get("/teachers", response_class=HTMLResponse)
async def teachers_page(request: Request):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    teachers = db.execute("SELECT * FROM teacher WHERE school_id=? ORDER BY name", (sid,)).fetchall()
    subjects  = db.execute("SELECT * FROM subject WHERE school_id=? ORDER BY name", (sid,)).fetchall()
    ts_map = {}
    for t in teachers:
        rows = db.execute("SELECT subject_id FROM teacher_subject WHERE teacher_id=?", (t["id"],)).fetchall()
        ts_map[t["id"]] = [r["subject_id"] for r in rows]
    db.close()
    return templates.TemplateResponse(request, "teachers.html",
        {"user": user, "teachers": teachers, "subjects": subjects, "teacher_subjects": ts_map})

@app.post("/teachers/add")
async def add_teacher(
    request: Request,
    name: str = Form(...), emp_code: str = Form(""),
    phone: str = Form(""), email: str = Form("")
):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    try:
        db.execute(
            "INSERT INTO teacher (school_id,name,emp_code,phone,email) VALUES (?,?,?,?,?)",
            (sid, name, emp_code or None, phone, email)
        )
        db.commit()
    except Exception as e:
        db.close()
        return HTMLResponse(f'<div class="toast error">✗ {e}</div>')
    teachers = db.execute("SELECT * FROM teacher WHERE school_id=? ORDER BY name", (sid,)).fetchall()
    subjects  = db.execute("SELECT * FROM subject WHERE school_id=? ORDER BY name", (sid,)).fetchall()
    ts_map = {}
    for t in teachers:
        rows = db.execute("SELECT subject_id FROM teacher_subject WHERE teacher_id=?", (t["id"],)).fetchall()
        ts_map[t["id"]] = [r["subject_id"] for r in rows]
    db.close()
    return templates.TemplateResponse(request, "partials/teacher_list.html",
        {"user": user, "teachers": teachers, "subjects": subjects, "teacher_subjects": ts_map})

@app.post("/teachers/{teacher_id}/subjects")
async def update_teacher_subjects(request: Request, teacher_id: int):
    user = get_current_user(request)
    if not user: return _redirect_login()
    form = await request.form()
    subject_ids = [int(v) for k, v in form.multi_items() if k == "subject_ids"]
    db = get_db()
    # Verify teacher belongs to school
    t = db.execute("SELECT id FROM teacher WHERE id=? AND school_id=?",
                   (teacher_id, user["school_id"])).fetchone()
    if t:
        db.execute("DELETE FROM teacher_subject WHERE teacher_id=?", (teacher_id,))
        for sid in subject_ids:
            db.execute("INSERT OR IGNORE INTO teacher_subject VALUES (?,?)", (teacher_id, sid))
        db.commit()
    db.close()
    return HTMLResponse('<div class="toast success">✓ Subjects updated!</div>')

@app.delete("/teachers/{teacher_id}")
async def delete_teacher(request: Request, teacher_id: int):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    db.execute("DELETE FROM teacher WHERE id=? AND school_id=?", (teacher_id, sid))
    db.commit()
    teachers = db.execute("SELECT * FROM teacher WHERE school_id=? ORDER BY name", (sid,)).fetchall()
    subjects  = db.execute("SELECT * FROM subject WHERE school_id=? ORDER BY name", (sid,)).fetchall()
    ts_map = {}
    for t in teachers:
        rows = db.execute("SELECT subject_id FROM teacher_subject WHERE teacher_id=?", (t["id"],)).fetchall()
        ts_map[t["id"]] = [r["subject_id"] for r in rows]
    db.close()
    return templates.TemplateResponse(request, "partials/teacher_list.html",
        {"user": user, "teachers": teachers, "subjects": subjects, "teacher_subjects": ts_map})

# ── Requirements ──────────────────────────────────────────────────────────────
@app.get("/requirements", response_class=HTMLResponse)
async def requirements_page(request: Request):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    classes  = db.execute("SELECT * FROM class_section WHERE school_id=? ORDER BY grade,section", (sid,)).fetchall()
    subjects = db.execute("SELECT * FROM subject WHERE school_id=? ORDER BY name", (sid,)).fetchall()
    teachers = db.execute("SELECT * FROM teacher WHERE school_id=? AND is_active=1 ORDER BY name", (sid,)).fetchall()
    reqs = db.execute(
        """SELECT csr.*, s.name as subj_name, s.subject_type, s.color,
                  t.name as teacher_name, cs.grade, cs.section
           FROM class_subject_requirement csr
           JOIN subject s        ON s.id  = csr.subject_id
           JOIN teacher t        ON t.id  = csr.teacher_id
           JOIN class_section cs ON cs.id = csr.class_id
           WHERE cs.school_id=? ORDER BY cs.grade,cs.section,s.name""",
        (sid,)
    ).fetchall()
    db.close()
    return templates.TemplateResponse(request, "requirements.html",
        {"user": user, "classes": classes, "subjects": subjects, "teachers": teachers, "reqs": reqs})

@app.post("/requirements/add")
async def add_requirement(
    request: Request,
    class_id: int = Form(...), subject_id: int = Form(...),
    teacher_id: int = Form(...), periods_per_week: int = Form(5)
):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    try:
        db.execute(
            """INSERT INTO class_subject_requirement (class_id,subject_id,teacher_id,periods_per_week)
               VALUES (?,?,?,?)
               ON CONFLICT(class_id,subject_id) DO UPDATE SET
               teacher_id=excluded.teacher_id, periods_per_week=excluded.periods_per_week""",
            (class_id, subject_id, teacher_id, periods_per_week)
        )
        db.commit()
    except Exception as e:
        db.close()
        return HTMLResponse(f'<div class="toast error">✗ {e}</div>')
    reqs = db.execute(
        """SELECT csr.*, s.name as subj_name, s.subject_type, s.color,
                  t.name as teacher_name, cs.grade, cs.section
           FROM class_subject_requirement csr
           JOIN subject s ON s.id=csr.subject_id
           JOIN teacher t ON t.id=csr.teacher_id
           JOIN class_section cs ON cs.id=csr.class_id
           WHERE cs.school_id=? ORDER BY cs.grade,cs.section,s.name""", (sid,)
    ).fetchall()
    db.close()
    return templates.TemplateResponse(request, "partials/req_list.html", {"reqs": reqs, "user": user})

@app.delete("/requirements/{req_id}")
async def delete_requirement(request: Request, req_id: int):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    db.execute("DELETE FROM class_subject_requirement WHERE id=?", (req_id,))
    db.commit()
    reqs = db.execute(
        """SELECT csr.*, s.name as subj_name, s.subject_type, s.color,
                  t.name as teacher_name, cs.grade, cs.section
           FROM class_subject_requirement csr
           JOIN subject s ON s.id=csr.subject_id
           JOIN teacher t ON t.id=csr.teacher_id
           JOIN class_section cs ON cs.id=csr.class_id
           WHERE cs.school_id=? ORDER BY cs.grade,cs.section,s.name""", (sid,)
    ).fetchall()
    db.close()
    return templates.TemplateResponse(request, "partials/req_list.html", {"reqs": reqs, "user": user})

# ── Generate ──────────────────────────────────────────────────────────────────
@app.post("/generate")
async def run_generate(request: Request):
    user = get_current_user(request)
    if not user: return _redirect_login()
    db  = get_db()
    cfg = _active_cfg(db, user["school_id"])
    db.close()
    if not cfg:
        return HTMLResponse('<div class="toast error">✗ Complete school setup first.</div>')
    result = generate_timetable(cfg["id"])
    html = f'<div class="toast {"success" if result["success"] else "error"}">{"✓" if result["success"] else "✗"} {result["message"]}</div>'
    if result.get("conflicts"):
        items = "".join(f"<li>{c}</li>" for c in result["conflicts"])
        html += f'<div class="alert-warn"><b>Constraints not fully met:</b><ul>{items}</ul></div>'
    return HTMLResponse(html)

# ── Timetable View ────────────────────────────────────────────────────────────
@app.get("/timetable", response_class=HTMLResponse)
async def timetable_view(request: Request, class_id: Optional[int] = None):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    cfg     = _active_cfg(db, sid)
    classes = db.execute("SELECT * FROM class_section WHERE school_id=? ORDER BY grade,section", (sid,)).fetchall()

    if not cfg or not classes:
        db.close()
        return templates.TemplateResponse(request, "timetable_view.html",
            {"user": user, "cfg": cfg, "classes": classes, "grid": {}, "days": [], "periods": [], "selected_class": None, "subjects": [], "teachers": [], "clashes": [], "clash_slots": set(), "break_periods": []})

    sel = class_id or classes[0]["id"]
    days   = _days(cfg["working_days"])
    n      = cfg["periods_per_day"]
    breaks = [int(x) for x in cfg["break_after"].split(",") if x.strip()]
    periods = list(range(1, n+1))

    rows = db.execute(
        """SELECT t.*, s.name as subj_name, s.subject_type, s.color, tc.name as teacher_name
           FROM timetable t
           LEFT JOIN subject s  ON s.id  = t.subject_id
           LEFT JOIN teacher tc ON tc.id = t.teacher_id
           WHERE t.config_id=? AND t.class_id=?""",
        (cfg["id"], sel)
    ).fetchall()

    grid = {(r["day"], r["period_no"]): dict(r) for r in rows}
    subjects = db.execute("SELECT * FROM subject WHERE school_id=? ORDER BY name", (sid,)).fetchall()
    teachers = db.execute("SELECT * FROM teacher WHERE school_id=? AND is_active=1 ORDER BY name", (sid,)).fetchall()
    clashes  = detect_clashes(cfg["id"])
    clash_slots = {(c["day"], c["period_no"], c["teacher_id"]) for c in clashes}
    db.close()
    return templates.TemplateResponse(request, "timetable_view.html", {
        "user": user, "cfg": cfg, "classes": classes, "selected_class": sel,
        "days": days, "periods": periods, "break_periods": breaks,
        "grid": grid, "subjects": subjects, "teachers": teachers,
        "clashes": clashes, "clash_slots": clash_slots,
    })

@app.post("/timetable/update-cell")
async def update_cell(
    request: Request,
    config_id: int = Form(...), class_id: int = Form(...),
    day: str = Form(...), period_no: int = Form(...),
    subject_id: Optional[int] = Form(None),
    teacher_id: Optional[int] = Form(None),
):
    user = get_current_user(request)
    if not user: return JSONResponse({"error": "Unauthorized"}, status_code=401)
    db = get_db()
    db.execute(
        """UPDATE timetable SET subject_id=?,teacher_id=?,is_free=?
           WHERE config_id=? AND class_id=? AND day=? AND period_no=?""",
        (subject_id, teacher_id, 1 if not subject_id else 0, config_id, class_id, day, period_no)
    )
    db.commit()
    clashes = detect_clashes(config_id)
    db.close()
    return JSONResponse({"clashes": clashes, "ok": True})

# ── Teacher Timetable ─────────────────────────────────────────────────────────
@app.get("/teacher-timetable", response_class=HTMLResponse)
async def teacher_timetable(request: Request, teacher_id: Optional[int] = None):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    cfg      = _active_cfg(db, sid)
    teachers = db.execute("SELECT * FROM teacher WHERE school_id=? AND is_active=1 ORDER BY name", (sid,)).fetchall()
    sel = teacher_id or (teachers[0]["id"] if teachers else None)
    days, periods, grid, breaks = [], [], {}, []

    if cfg and sel:
        days    = _days(cfg["working_days"])
        breaks  = [int(x) for x in cfg["break_after"].split(",") if x.strip()]
        periods = list(range(1, cfg["periods_per_day"]+1))
        rows = db.execute(
            """SELECT t.*, s.name as subj_name, s.subject_type, cs.grade, cs.section
               FROM timetable t
               LEFT JOIN subject s        ON s.id  = t.subject_id
               LEFT JOIN class_section cs ON cs.id = t.class_id
               WHERE t.config_id=? AND t.teacher_id=?""",
            (cfg["id"], sel)
        ).fetchall()
        grid = {(r["day"], r["period_no"]): dict(r) for r in rows}
    db.close()
    return templates.TemplateResponse(request, "teacher_timetable.html", {
        "user": user, "teachers": teachers, "selected_teacher": sel,
        "days": days, "periods": periods, "break_periods": breaks, "grid": grid, "cfg": cfg,
    })

# ── Substitution ───────────────────────────────────────────────────────────────
@app.get("/substitute", response_class=HTMLResponse)
async def substitute_page(request: Request):
    user = get_current_user(request)
    if not user: return _redirect_login()
    db = get_db()
    teachers = db.execute("SELECT * FROM teacher WHERE school_id=? AND is_active=1 ORDER BY name", (user["school_id"],)).fetchall()
    db.close()
    return templates.TemplateResponse(request, "substitute.html", {"user": user, "teachers": teachers})

@app.post("/substitute/find")
async def find_substitute(
    request: Request,
    absent_teacher_id: int = Form(...), day: str = Form(...)
):
    user = get_current_user(request)
    if not user: return _redirect_login()
    db  = get_db()
    cfg = _active_cfg(db, user["school_id"])
    teachers = db.execute("SELECT * FROM teacher WHERE school_id=? AND is_active=1 ORDER BY name", (user["school_id"],)).fetchall()
    db.close()
    if not cfg:
        return HTMLResponse('<div class="toast error">✗ No timetable generated yet.</div>')
    suggestions = get_substitute_suggestions(cfg["id"], absent_teacher_id, day)
    return templates.TemplateResponse(request, "partials/substitute_suggestions.html",
        {"suggestions": suggestions, "day": day, "config_id": cfg["id"], "user": user})

@app.post("/substitute/assign")
async def assign_substitute(
    request: Request,
    config_id: int = Form(...), absent_teacher_id: int = Form(...),
    substitute_teacher_id: int = Form(...), class_id: int = Form(...),
    day: str = Form(...), period_no: int = Form(...), subject_id: int = Form(...),
):
    user = get_current_user(request)
    if not user: return _redirect_login()
    db = get_db()
    db.execute(
        "INSERT INTO substitution (config_id,absent_teacher_id,substitute_teacher_id,class_id,day,period_no) VALUES (?,?,?,?,?,?)",
        (config_id, absent_teacher_id, substitute_teacher_id, class_id, day, period_no)
    )
    db.execute(
        "UPDATE timetable SET teacher_id=? WHERE config_id=? AND class_id=? AND day=? AND period_no=?",
        (substitute_teacher_id, config_id, class_id, day, period_no)
    )
    db.commit(); db.close()
    return HTMLResponse('<div class="toast success">✓ Substitute assigned!</div>')

# ── Team Management (owner/admin adds staff) ──────────────────────────────────
@app.get("/team", response_class=HTMLResponse)
async def team_page(request: Request):
    user = get_current_user(request)
    if not user: return _redirect_login()
    if user["role"] not in ("owner", "admin"):
        return RedirectResponse("/dashboard", status_code=302)
    db = get_db()
    members = db.execute(
        "SELECT * FROM user WHERE school_id=? ORDER BY role,name", (user["school_id"],)
    ).fetchall()
    db.close()
    return templates.TemplateResponse(request, "team.html", {"user": user, "members": members})

@app.post("/team/add")
async def add_team_member(
    request: Request,
    name: str = Form(...), email: str = Form(...),
    password: str = Form(...), role: str = Form("staff")
):
    user = get_current_user(request)
    if not user: return _redirect_login()
    if user["role"] not in ("owner", "admin"):
        return HTMLResponse('<div class="toast error">✗ Permission denied.</div>')
    ok, msg = invite_user(user["school_id"], name, email, password, role)
    if not ok:
        return HTMLResponse(f'<div class="toast error">✗ {msg}</div>')
    db = get_db()
    members = db.execute(
        "SELECT * FROM user WHERE school_id=? ORDER BY role,name", (user["school_id"],)
    ).fetchall()
    db.close()
    return templates.TemplateResponse(request, "partials/team_list.html", {"members": members, "user": user})

@app.delete("/team/{user_id}")
async def remove_team_member(request: Request, user_id: int):
    user = get_current_user(request)
    if not user: return _redirect_login()
    if user["role"] != "owner":
        return HTMLResponse('<div class="toast error">✗ Only owner can remove members.</div>')
    if user_id == user["user_id"]:
        return HTMLResponse('<div class="toast error">✗ Cannot remove yourself.</div>')
    db = get_db()
    db.execute("DELETE FROM user WHERE id=? AND school_id=?", (user_id, user["school_id"]))
    db.commit()
    members = db.execute(
        "SELECT * FROM user WHERE school_id=? ORDER BY role,name", (user["school_id"],)
    ).fetchall()
    db.close()
    return templates.TemplateResponse(request, "partials/team_list.html", {"members": members, "user": user})

# ── Account / Change Password ─────────────────────────────────────────────────
@app.get("/account", response_class=HTMLResponse)
async def account_page(request: Request):
    user = get_current_user(request)
    if not user: return _redirect_login()
    return templates.TemplateResponse(request, "account.html", {"user": user})

@app.post("/account/password")
async def change_password(
    request: Request,
    current_password: str = Form(...),
    new_password: str = Form(...),
    confirm_password: str = Form(...),
):
    user = get_current_user(request)
    if not user: return _redirect_login()
    if new_password != confirm_password:
        return HTMLResponse('<div class="toast error">✗ Passwords do not match.</div>')
    if len(new_password) < 8:
        return HTMLResponse('<div class="toast error">✗ Password must be at least 8 characters.</div>')
    db = get_db()
    u = db.execute("SELECT password_hash FROM user WHERE id=?", (user["user_id"],)).fetchone()
    if not verify_password(current_password, u["password_hash"]):
        db.close()
        return HTMLResponse('<div class="toast error">✗ Current password is incorrect.</div>')
    db.execute("UPDATE user SET password_hash=? WHERE id=?",
               (hash_password(new_password), user["user_id"]))
    db.commit(); db.close()
    return HTMLResponse('<div class="toast success">✓ Password changed successfully!</div>')

# ── PDF Exports ────────────────────────────────────────────────────────────────
@app.get("/export/class/{class_id}/pdf")
async def export_class_pdf(request: Request, class_id: int):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    cfg    = _active_cfg(db, sid)
    school = db.execute("SELECT * FROM school WHERE id=?", (sid,)).fetchone()
    cls    = db.execute("SELECT * FROM class_section WHERE id=? AND school_id=?", (class_id, sid)).fetchone()
    if not cfg or not cls:
        db.close(); raise HTTPException(404)
    days    = _days(cfg["working_days"])
    periods = list(range(1, cfg["periods_per_day"]+1))
    rows = db.execute(
        """SELECT t.*, s.name as subject, s.subject_type as subj_type, tc.name as teacher
           FROM timetable t
           LEFT JOIN subject s  ON s.id  = t.subject_id
           LEFT JOIN teacher tc ON tc.id = t.teacher_id
           WHERE t.config_id=? AND t.class_id=?""",
        (cfg["id"], class_id)
    ).fetchall()
    db.close()
    grid = {(r["day"], r["period_no"]): dict(r) for r in rows}
    pdf  = export_class_timetable_pdf(school["name"], cls["grade"], cls["section"],
                                      days, periods, grid, cfg["academic_year"])
    return Response(content=pdf, media_type="application/pdf",
        headers={"Content-Disposition": f"attachment; filename=timetable_{cls['grade']}{cls['section']}.pdf"})

@app.get("/export/teacher/{teacher_id}/pdf")
async def export_teacher_pdf(request: Request, teacher_id: int):
    user = get_current_user(request)
    if not user: return _redirect_login()
    sid = user["school_id"]
    db  = get_db()
    cfg     = _active_cfg(db, sid)
    school  = db.execute("SELECT * FROM school WHERE id=?", (sid,)).fetchone()
    teacher = db.execute("SELECT * FROM teacher WHERE id=? AND school_id=?", (teacher_id, sid)).fetchone()
    if not cfg or not teacher:
        db.close(); raise HTTPException(404)
    days    = _days(cfg["working_days"])
    periods = list(range(1, cfg["periods_per_day"]+1))
    rows = db.execute(
        """SELECT t.*, s.name as subject, s.subject_type as subj_type, cs.grade, cs.section
           FROM timetable t
           LEFT JOIN subject s        ON s.id  = t.subject_id
           LEFT JOIN class_section cs ON cs.id = t.class_id
           WHERE t.config_id=? AND t.teacher_id=?""",
        (cfg["id"], teacher_id)
    ).fetchall()
    db.close()
    grid = {(r["day"], r["period_no"]): dict(r) for r in rows}
    pdf  = export_teacher_timetable_pdf(school["name"], teacher["name"],
                                        days, periods, grid, cfg["academic_year"])
    return Response(content=pdf, media_type="application/pdf",
        headers={"Content-Disposition": f"attachment; filename=timetable_{teacher['name'].replace(' ','_')}.pdf"})

# ── API ────────────────────────────────────────────────────────────────────────
@app.get("/api/clashes")
async def api_clashes(request: Request):
    user = get_current_user(request)
    if not user: return JSONResponse([])
    db  = get_db()
    cfg = _active_cfg(db, user["school_id"])
    db.close()
    return JSONResponse(detect_clashes(cfg["id"]) if cfg else [])

# ── Platform Admin ─────────────────────────────────────────────────────────────
@app.get("/admin", response_class=HTMLResponse)
async def admin_dashboard(request: Request, secret: str = ""):
    # Simple secret-key admin — replace with proper admin auth in production
    admin_secret = os.environ.get("ADMIN_SECRET", "changeme123")
    if secret != admin_secret:
        return HTMLResponse("<h2>Access denied</h2>", status_code=403)
    db = get_db()
    schools = db.execute(
        """SELECT s.*, COUNT(u.id) as user_count
           FROM school s LEFT JOIN user u ON u.school_id=s.id
           GROUP BY s.id ORDER BY s.created_at DESC"""
    ).fetchall()
    total_users = db.execute("SELECT COUNT(*) FROM user").fetchone()[0]
    db.close()
    return templates.TemplateResponse(request, "admin.html",
        {"schools": schools, "total_users": total_users, "secret": secret})
