"""
Invitations & People Router.

POST   /users/invite               — school admin sends magic-link invitation
GET    /users/invitations          — list invitations for the school
DELETE /users/invitations/{id}     — cancel a pending invitation
POST   /users/invitations/{id}/resend — re-send magic link
GET    /users/staff                — list current staff members
GET    /users/students             — list current student members
"""
import os
from datetime import datetime, timezone, timedelta
from typing import Any, Dict, List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, EmailStr
from modules.logger_tool import initialise_logger
from modules.auth.supabase_bearer import SupabaseBearer
from modules.database.supabase.utils.client import SupabaseServiceRoleClient

logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True)
router = APIRouter()

VALID_ROLES = {"teacher", "student", "school_admin", "department_head"}
STAFF_ROLES = {"teacher", "school_admin", "department_head"}


def _sb() -> SupabaseServiceRoleClient:
    return SupabaseServiceRoleClient()


def _resolve_institute_id(user_id: str) -> Optional[str]:
    try:
        sb = _sb()
        p = sb.supabase.table("profiles").select("school_id").eq("id", user_id).single().execute()
        return str((p.data or {}).get("school_id") or "") or None
    except Exception:
        return None


def _require_institute(user_id: str) -> str:
    iid = _resolve_institute_id(user_id)
    if not iid:
        raise HTTPException(status_code=400, detail="User is not linked to a school")
    return iid


def _require_school_admin(user_id: str, institute_id: str) -> None:
    try:
        sb = _sb()
        m = (
            sb.supabase.table("institute_memberships")
            .select("role")
            .eq("profile_id", user_id)
            .eq("institute_id", institute_id)
            .single()
            .execute()
        )
        role = (m.data or {}).get("role", "")
        if role not in ("school_admin", "department_head"):
            raise HTTPException(status_code=403, detail="School admin access required")
    except HTTPException:
        raise
    except Exception:
        raise HTTPException(status_code=403, detail="Could not verify school admin status")


# ─── Request models ───────────────────────────────────────────────────────────

class InviteRequest(BaseModel):
    email: str
    role: str                              # teacher | student | school_admin | department_head
    metadata: Optional[Dict[str, Any]] = None  # year_group, subject, department, etc.


# ─── Invite ───────────────────────────────────────────────────────────────────

@router.post("/invite")
async def invite_user(
    body: InviteRequest,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    """
    Create an invitation row and send a Supabase magic-link invite email.
    Idempotent: if a pending invitation already exists for the same email/school,
    it is returned (use /resend to refresh the magic link).
    """
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    _require_school_admin(user_id, institute_id)

    if body.role not in VALID_ROLES:
        raise HTTPException(status_code=400, detail=f"role must be one of {sorted(VALID_ROLES)}")

    email = body.email.strip().lower()
    sb = _sb()

    # Check for existing pending invitation
    existing = (
        sb.supabase.table("invitations")
        .select("id,status,email,role,created_at,expires_at")
        .eq("institute_id", institute_id)
        .eq("email", email)
        .eq("status", "pending")
        .execute()
        .data or []
    )
    if existing:
        return {
            "status": "already_pending",
            "invitation": existing[0],
            "message": "A pending invitation already exists. Use /resend to refresh the magic link.",
        }

    # Insert invitation row
    expires_at = (datetime.now(timezone.utc) + timedelta(days=7)).isoformat()
    inv_data = {
        "institute_id": institute_id,
        "email": email,
        "role": body.role,
        "invited_by": user_id,
        "expires_at": expires_at,
        "status": "pending",
        "metadata": body.metadata or {},
    }
    inv_res = sb.supabase.table("invitations").insert(inv_data).execute()
    if not inv_res.data:
        raise HTTPException(status_code=500, detail="Failed to create invitation record")
    invitation = inv_res.data[0]

    # Send magic link via Supabase Auth admin
    try:
        sb.supabase.auth.admin.invite_user_by_email(
            email,
            options={
                "data": {
                    "invitation_id": invitation["id"],
                    "institute_id": institute_id,
                    "role": body.role,
                    **(body.metadata or {}),
                }
            },
        )
        magic_link_sent = True
    except Exception as e:
        logger.warning(f"Magic link send failed for {email}: {e}")
        magic_link_sent = False

    logger.info(f"Invited {email} as {body.role} to school {institute_id} by {user_id}")
    return {
        "status": "ok",
        "invitation": invitation,
        "magic_link_sent": magic_link_sent,
    }


# ─── List invitations ─────────────────────────────────────────────────────────

@router.get("/invitations")
async def list_invitations(
    role: Optional[str] = Query(None),
    status: Optional[str] = Query(None, description="pending|accepted|expired|cancelled"),
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    _require_school_admin(user_id, institute_id)

    sb = _sb()
    q = (
        sb.supabase.table("invitations")
        .select("id,email,role,status,invited_by,expires_at,created_at,metadata")
        .eq("institute_id", institute_id)
        .order("created_at", desc=True)
    )
    if role:
        q = q.eq("role", role)
    if status:
        q = q.eq("status", status)

    rows = q.execute().data or []

    # Mark expired rows (status still 'pending' but past expires_at)
    now = datetime.now(timezone.utc)
    for row in rows:
        if row["status"] == "pending":
            try:
                exp = datetime.fromisoformat(row["expires_at"].replace("Z", "+00:00"))
                if exp < now:
                    row["status"] = "expired"
            except Exception:
                pass

    return {"status": "ok", "invitations": rows, "total": len(rows)}


# ─── Cancel invitation ────────────────────────────────────────────────────────

@router.delete("/invitations/{invitation_id}")
async def cancel_invitation(
    invitation_id: str,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    _require_school_admin(user_id, institute_id)

    sb = _sb()
    res = (
        sb.supabase.table("invitations")
        .update({"status": "cancelled"})
        .eq("id", invitation_id)
        .eq("institute_id", institute_id)
        .eq("status", "pending")
        .execute()
    )
    if not res.data:
        raise HTTPException(status_code=404, detail="Pending invitation not found")
    return {"status": "ok", "invitation": res.data[0]}


# ─── Resend invitation ────────────────────────────────────────────────────────

@router.post("/invitations/{invitation_id}/resend")
async def resend_invitation(
    invitation_id: str,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    """Re-trigger the magic link for a pending (or expired) invitation."""
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    _require_school_admin(user_id, institute_id)

    sb = _sb()
    inv = (
        sb.supabase.table("invitations")
        .select("*")
        .eq("id", invitation_id)
        .eq("institute_id", institute_id)
        .single()
        .execute()
    ).data
    if not inv:
        raise HTTPException(status_code=404, detail="Invitation not found")
    if inv["status"] == "accepted":
        raise HTTPException(status_code=400, detail="Invitation already accepted")
    if inv["status"] == "cancelled":
        raise HTTPException(status_code=400, detail="Invitation was cancelled — create a new one")

    # Refresh expiry + status
    new_expires = (datetime.now(timezone.utc) + timedelta(days=7)).isoformat()
    sb.supabase.table("invitations").update({
        "expires_at": new_expires,
        "status": "pending",
    }).eq("id", invitation_id).execute()

    # Re-send magic link
    try:
        sb.supabase.auth.admin.invite_user_by_email(
            inv["email"],
            options={
                "data": {
                    "invitation_id": invitation_id,
                    "institute_id": institute_id,
                    "role": inv["role"],
                    **inv.get("metadata", {}),
                }
            },
        )
        magic_link_sent = True
    except Exception as e:
        logger.warning(f"Resend magic link failed for {inv['email']}: {e}")
        magic_link_sent = False

    return {"status": "ok", "magic_link_sent": magic_link_sent, "new_expires_at": new_expires}


# ─── Staff list ───────────────────────────────────────────────────────────────

@router.get("/staff")
async def list_staff(
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    """List all staff members (teachers, admins, department heads) in the school."""
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    _require_school_admin(user_id, institute_id)

    sb = _sb()
    members = (
        sb.supabase.table("institute_memberships")
        .select("profile_id,role,joined_at")
        .eq("institute_id", institute_id)
        .in_("role", list(STAFF_ROLES))
        .execute()
        .data or []
    )

    if not members:
        return {"status": "ok", "staff": [], "total": 0}

    profile_ids = [m["profile_id"] for m in members]
    profiles = (
        sb.supabase.table("profiles")
        .select("id,email,username,display_name")
        .in_("id", profile_ids)
        .execute()
        .data or []
    )
    profile_map = {p["id"]: p for p in profiles}

    staff = []
    for m in members:
        p = profile_map.get(m["profile_id"], {})
        staff.append({
            "profile_id": m["profile_id"],
            "email": p.get("email"),
            "username": p.get("username"),
            "display_name": p.get("display_name"),
            "role": m["role"],
            "joined_at": m["joined_at"],
        })

    return {"status": "ok", "staff": staff, "total": len(staff)}


# ─── Student list ─────────────────────────────────────────────────────────────

@router.get("/students")
async def list_students(
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    """List all student members in the school."""
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    _require_school_admin(user_id, institute_id)

    sb = _sb()
    members = (
        sb.supabase.table("institute_memberships")
        .select("profile_id,role,joined_at")
        .eq("institute_id", institute_id)
        .eq("role", "student")
        .execute()
        .data or []
    )

    if not members:
        return {"status": "ok", "students": [], "total": 0}

    profile_ids = [m["profile_id"] for m in members]
    profiles = (
        sb.supabase.table("profiles")
        .select("id,email,username,display_name")
        .in_("id", profile_ids)
        .execute()
        .data or []
    )
    profile_map = {p["id"]: p for p in profiles}

    students = []
    for m in members:
        p = profile_map.get(m["profile_id"], {})
        students.append({
            "profile_id": m["profile_id"],
            "email": p.get("email"),
            "username": p.get("username"),
            "display_name": p.get("display_name"),
            "role": m["role"],
            "joined_at": m["joined_at"],
        })

    return {"status": "ok", "students": students, "total": len(students)}
