import os
import uuid
import aiohttp
from datetime import datetime
from typing import Any, Dict, List, Optional
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
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()

_OLLAMA_URL = os.getenv("OLLAMA_URL", "http://localhost:11434")
_OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "llama3")
_LLM_TIMEOUT = 60

VALID_STATUS = {"draft", "ready", "archived"}
VALID_FIELDS = {"objectives", "activity_description", "title"}
BLOOM_LEVELS = {"remember", "understand", "apply", "analyse", "evaluate", "create"}


# ─── Supabase helpers ─────────────────────────────────────────────────────────

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 _is_creator(plan: Dict[str, Any], user_id: str) -> bool:
    return str(plan.get("created_by", "")) == str(user_id)


def _can_edit_plan(sb: SupabaseServiceRoleClient, plan_id: str, user_id: str) -> bool:
    try:
        plan_res = (
            sb.supabase.table("planned_lessons")
            .select("created_by")
            .eq("id", plan_id)
            .single()
            .execute()
        )
        if not plan_res.data:
            return False
        if str(plan_res.data.get("created_by", "")) == str(user_id):
            return True
        collab = (
            sb.supabase.table("lesson_collaborators")
            .select("can_edit")
            .eq("planned_lesson_id", plan_id)
            .eq("profile_id", user_id)
            .single()
            .execute()
        )
        return bool((collab.data or {}).get("can_edit", False))
    except Exception:
        return False


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

class CreatePlanRequest(BaseModel):
    title: str
    class_id: Optional[str] = None
    subject: Optional[str] = None
    year_group: Optional[str] = None
    estimated_duration_minutes: Optional[int] = None
    objectives: Optional[List[Dict[str, Any]]] = None
    activities: Optional[List[Dict[str, Any]]] = None
    status: Optional[str] = "draft"
    tags: Optional[List[str]] = None
    topic_code: Optional[str] = None
    whiteboard_room_id: Optional[str] = None
    course_id: Optional[str] = None
    sequence_number: Optional[int] = None


class UpdatePlanRequest(BaseModel):
    title: Optional[str] = None
    class_id: Optional[str] = None
    subject: Optional[str] = None
    year_group: Optional[str] = None
    estimated_duration_minutes: Optional[int] = None
    objectives: Optional[List[Dict[str, Any]]] = None
    activities: Optional[List[Dict[str, Any]]] = None
    status: Optional[str] = None
    tags: Optional[List[str]] = None
    topic_code: Optional[str] = None
    whiteboard_room_id: Optional[str] = None
    course_id: Optional[str] = None
    sequence_number: Optional[int] = None


class DeliverPlanRequest(BaseModel):
    taught_lesson_id: Optional[str] = None
    class_id: Optional[str] = None
    notes: Optional[str] = None


class AddCollaboratorRequest(BaseModel):
    profile_id: str
    can_edit: bool = True


class SuggestRequest(BaseModel):
    field: str
    context: Optional[str] = None
    activity_section: Optional[str] = None
    objective_texts: Optional[List[str]] = None


# ─── Endpoints ────────────────────────────────────────────────────────────────

@router.get("/plans")
async def list_plans(
    class_id: Optional[str] = None,
    status: Optional[str] = None,
    subject: Optional[str] = None,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    sb = _sb()

    q = (
        sb.supabase.table("planned_lessons")
        .select("*")
        .eq("institute_id", institute_id)
    )
    if class_id:
        q = q.eq("class_id", class_id)
    if status:
        q = q.eq("status", status)
    if subject:
        q = q.eq("subject", subject)

    owned = q.eq("created_by", user_id).execute().data or []

    collab_rows = (
        sb.supabase.table("lesson_collaborators")
        .select("planned_lesson_id")
        .eq("profile_id", user_id)
        .execute()
        .data or []
    )
    collab_plan_ids = [r["planned_lesson_id"] for r in collab_rows]

    collab_plans: List[Dict] = []
    if collab_plan_ids:
        q2 = (
            sb.supabase.table("planned_lessons")
            .select("*")
            .eq("institute_id", institute_id)
            .in_("id", collab_plan_ids)
        )
        if class_id:
            q2 = q2.eq("class_id", class_id)
        if status:
            q2 = q2.eq("status", status)
        if subject:
            q2 = q2.eq("subject", subject)
        collab_plans = q2.execute().data or []

    seen_ids: set = set()
    all_plans: List[Dict] = []
    for p in owned + collab_plans:
        if p["id"] not in seen_ids:
            seen_ids.add(p["id"])
            all_plans.append(p)

    if not all_plans:
        return {"plans": []}

    plan_ids = [p["id"] for p in all_plans]

    collab_counts: Dict[str, int] = {}
    delivery_counts: Dict[str, int] = {}

    try:
        cc = (
            sb.supabase.table("lesson_collaborators")
            .select("planned_lesson_id")
            .in_("planned_lesson_id", plan_ids)
            .execute()
            .data or []
        )
        for r in cc:
            collab_counts[r["planned_lesson_id"]] = collab_counts.get(r["planned_lesson_id"], 0) + 1
    except Exception:
        pass

    try:
        dc = (
            sb.supabase.table("lesson_deliveries")
            .select("planned_lesson_id")
            .in_("planned_lesson_id", plan_ids)
            .execute()
            .data or []
        )
        for r in dc:
            delivery_counts[r["planned_lesson_id"]] = delivery_counts.get(r["planned_lesson_id"], 0) + 1
    except Exception:
        pass

    enriched = [
        {
            **p,
            "collaborator_count": collab_counts.get(p["id"], 0),
            "delivery_count": delivery_counts.get(p["id"], 0),
            "is_owner": str(p.get("created_by", "")) == str(user_id),
        }
        for p in all_plans
    ]
    return {"plans": enriched}


@router.post("/plans")
async def create_plan(
    body: CreatePlanRequest,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    sb = _sb()

    if body.status and body.status not in VALID_STATUS:
        raise HTTPException(status_code=400, detail=f"status must be one of {VALID_STATUS}")

    row: Dict[str, Any] = {
        "created_by": user_id,
        "institute_id": institute_id,
        "title": body.title,
        "status": body.status or "draft",
        "objectives": body.objectives or [],
        "activities": body.activities or [],
        "tags": body.tags or [],
    }
    for field in (
        "class_id", "subject", "year_group", "estimated_duration_minutes",
        "topic_code", "whiteboard_room_id", "course_id", "sequence_number",
    ):
        val = getattr(body, field)
        if val is not None:
            row[field] = val

    res = sb.supabase.table("planned_lessons").insert(row).execute()
    plan = (res.data or [{}])[0]
    logger.info(f"Lesson plan created: {plan.get('id')} '{body.title}' by {user_id}")
    return plan


@router.get("/plans/{plan_id}")
async def get_plan(
    plan_id: str,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    sb = _sb()

    plan_res = (
        sb.supabase.table("planned_lessons")
        .select("*")
        .eq("id", plan_id)
        .eq("institute_id", institute_id)
        .single()
        .execute()
    )
    if not plan_res.data:
        raise HTTPException(status_code=404, detail="Lesson plan not found")

    plan = plan_res.data
    if not _is_creator(plan, user_id):
        collab_check = (
            sb.supabase.table("lesson_collaborators")
            .select("can_edit")
            .eq("planned_lesson_id", plan_id)
            .eq("profile_id", user_id)
            .execute()
            .data
        )
        if collab_check is None:
            raise HTTPException(status_code=403, detail="Access denied")

    collab_rows = (
        sb.supabase.table("lesson_collaborators")
        .select("profile_id, can_edit, added_at")
        .eq("planned_lesson_id", plan_id)
        .execute()
        .data or []
    )

    profile_ids = [r["profile_id"] for r in collab_rows]
    profile_map: Dict[str, Dict] = {}
    if profile_ids:
        try:
            profiles = (
                sb.supabase.table("profiles")
                .select("id, full_name, email")
                .in_("id", profile_ids)
                .execute()
                .data or []
            )
            profile_map = {p["id"]: p for p in profiles}
        except Exception:
            pass

    collaborators = [
        {
            **r,
            "full_name": profile_map.get(r["profile_id"], {}).get("full_name"),
            "email": profile_map.get(r["profile_id"], {}).get("email"),
        }
        for r in collab_rows
    ]

    deliveries = (
        sb.supabase.table("lesson_deliveries")
        .select("*")
        .eq("planned_lesson_id", plan_id)
        .order("started_at", desc=True)
        .limit(10)
        .execute()
        .data or []
    )

    return {**plan, "collaborators": collaborators, "recent_deliveries": deliveries}


@router.patch("/plans/{plan_id}")
async def update_plan(
    plan_id: str,
    body: UpdatePlanRequest,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    sb = _sb()

    if not _can_edit_plan(sb, plan_id, user_id):
        raise HTTPException(status_code=403, detail="Access denied — not creator or editor collaborator")

    if body.status and body.status not in VALID_STATUS:
        raise HTTPException(status_code=400, detail=f"status must be one of {VALID_STATUS}")

    updates: Dict[str, Any] = {}
    for field in (
        "title", "class_id", "subject", "year_group", "estimated_duration_minutes",
        "objectives", "activities", "status", "tags", "topic_code",
        "whiteboard_room_id", "course_id", "sequence_number",
    ):
        val = getattr(body, field)
        if val is not None:
            updates[field] = val

    if not updates:
        raise HTTPException(status_code=400, detail="Nothing to update")

    updates["updated_at"] = datetime.utcnow().isoformat()
    res = (
        sb.supabase.table("planned_lessons")
        .update(updates)
        .eq("id", plan_id)
        .eq("institute_id", institute_id)
        .execute()
    )
    if not res.data:
        raise HTTPException(status_code=404, detail="Plan not found")
    return res.data[0]


@router.delete("/plans/{plan_id}")
async def delete_plan(
    plan_id: str,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    sb = _sb()

    plan_res = (
        sb.supabase.table("planned_lessons")
        .select("created_by")
        .eq("id", plan_id)
        .eq("institute_id", institute_id)
        .single()
        .execute()
    )
    if not plan_res.data:
        raise HTTPException(status_code=404, detail="Plan not found")
    if not _is_creator(plan_res.data, user_id):
        raise HTTPException(status_code=403, detail="Only the creator can delete a plan")

    sb.supabase.table("lesson_collaborators").delete().eq("planned_lesson_id", plan_id).execute()
    sb.supabase.table("planned_lessons").delete().eq("id", plan_id).execute()
    logger.info(f"Lesson plan deleted: {plan_id} by {user_id}")
    return {"status": "ok"}


@router.post("/plans/{plan_id}/deliver")
async def deliver_plan(
    plan_id: str,
    body: DeliverPlanRequest,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    sb = _sb()

    plan_res = (
        sb.supabase.table("planned_lessons")
        .select("id, whiteboard_room_id")
        .eq("id", plan_id)
        .eq("institute_id", institute_id)
        .single()
        .execute()
    )
    if not plan_res.data:
        raise HTTPException(status_code=404, detail="Plan not found")

    if not _can_edit_plan(sb, plan_id, user_id):
        raise HTTPException(status_code=403, detail="Access denied")

    row: Dict[str, Any] = {
        "planned_lesson_id": plan_id,
        "delivered_by": user_id,
        "institute_id": institute_id,
        "started_at": datetime.utcnow().isoformat(),
    }
    if body.taught_lesson_id:
        row["taught_lesson_id"] = body.taught_lesson_id
    if body.class_id:
        row["class_id"] = body.class_id
    if body.notes:
        row["notes"] = body.notes

    whiteboard_room_id = plan_res.data.get("whiteboard_room_id")
    if whiteboard_room_id:
        row["whiteboard_room_id"] = whiteboard_room_id

    res = sb.supabase.table("lesson_deliveries").insert(row).execute()
    delivery = (res.data or [{}])[0]
    logger.info(f"Lesson delivery created: {delivery.get('id')} for plan {plan_id} by {user_id}")
    return delivery


@router.get("/plans/{plan_id}/deliveries")
async def list_deliveries(
    plan_id: str,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    sb = _sb()

    plan_res = (
        sb.supabase.table("planned_lessons")
        .select("created_by")
        .eq("id", plan_id)
        .eq("institute_id", institute_id)
        .single()
        .execute()
    )
    if not plan_res.data:
        raise HTTPException(status_code=404, detail="Plan not found")

    deliveries = (
        sb.supabase.table("lesson_deliveries")
        .select("*")
        .eq("planned_lesson_id", plan_id)
        .order("started_at", desc=True)
        .execute()
        .data or []
    )
    return {"deliveries": deliveries}


@router.post("/plans/{plan_id}/collaborators")
async def add_collaborator(
    plan_id: str,
    body: AddCollaboratorRequest,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    sb = _sb()

    plan_res = (
        sb.supabase.table("planned_lessons")
        .select("created_by")
        .eq("id", plan_id)
        .eq("institute_id", institute_id)
        .single()
        .execute()
    )
    if not plan_res.data:
        raise HTTPException(status_code=404, detail="Plan not found")
    if not _is_creator(plan_res.data, user_id):
        raise HTTPException(status_code=403, detail="Only the creator can add collaborators")

    res = (
        sb.supabase.table("lesson_collaborators")
        .upsert(
            {
                "planned_lesson_id": plan_id,
                "profile_id": body.profile_id,
                "can_edit": body.can_edit,
                "added_at": datetime.utcnow().isoformat(),
            },
            on_conflict="planned_lesson_id,profile_id",
        )
        .execute()
    )
    return {"status": "ok", "row": (res.data or [{}])[0]}


@router.delete("/plans/{plan_id}/collaborators/{profile_id}")
async def remove_collaborator(
    plan_id: str,
    profile_id: str,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    sb = _sb()

    plan_res = (
        sb.supabase.table("planned_lessons")
        .select("created_by")
        .eq("id", plan_id)
        .eq("institute_id", institute_id)
        .single()
        .execute()
    )
    if not plan_res.data:
        raise HTTPException(status_code=404, detail="Plan not found")
    if not _is_creator(plan_res.data, user_id):
        raise HTTPException(status_code=403, detail="Only the creator can remove collaborators")

    sb.supabase.table("lesson_collaborators").delete().eq("planned_lesson_id", plan_id).eq("profile_id", profile_id).execute()
    return {"status": "ok"}


@router.post("/plans/{plan_id}/suggest")
async def suggest_field(
    plan_id: str,
    body: SuggestRequest,
    credentials: dict = Depends(SupabaseBearer()),
) -> Dict[str, Any]:
    user_id = credentials.get("sub", "")
    institute_id = _require_institute(user_id)
    sb = _sb()

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

    plan_res = (
        sb.supabase.table("planned_lessons")
        .select("*")
        .eq("id", plan_id)
        .eq("institute_id", institute_id)
        .single()
        .execute()
    )
    if not plan_res.data:
        raise HTTPException(status_code=404, detail="Plan not found")

    if not _can_edit_plan(sb, plan_id, user_id):
        raise HTTPException(status_code=403, detail="Access denied")

    plan = plan_res.data
    subject = plan.get("subject") or "a UK secondary school subject"
    year_group = plan.get("year_group") or "unspecified year group"
    title = plan.get("title", "")
    context = body.context or ""

    context_part = ("Teacher notes: " + context + "\n") if context else ""
    if body.field == "objectives":
        existing = ""
        if body.objective_texts:
            existing = "\n".join("- " + t for t in body.objective_texts)
        objectives_part = ("Existing objectives:\n" + existing + "\n") if existing else ""
        prompt = (
            "You are an expert UK secondary school teacher.\n"
            "Lesson: '" + title + "', Subject: " + subject + ", Year: " + year_group + ".\n"
            + objectives_part + context_part
            + "Suggest ONE clear, measurable learning objective for this lesson using Bloom's taxonomy. "
            "Write only the objective text, no bullet point or prefix."
        )
    elif body.field == "activity_description":
        section = body.activity_section or "Main"
        prompt = (
            "You are an expert UK secondary school teacher.\n"
            "Lesson: '" + title + "', Subject: " + subject + ", Year: " + year_group + ".\n"
            "Activity section: " + section + ".\n"
            + context_part
            + "Write a concise, practical description (2-4 sentences) for a " + section + " activity "
            "suitable for UK secondary pupils. Include what the teacher does and what pupils do."
        )
    else:  # title
        prompt = (
            "You are an expert UK secondary school teacher.\n"
            "Subject: " + subject + ", Year: " + year_group + ".\n"
            + context_part
            + "Suggest ONE engaging, clear lesson title for a UK secondary school lesson. "
            "Write only the title, no explanation."
        )

    try:
        payload = {
            "model": _OLLAMA_MODEL,
            "prompt": prompt,
            "stream": False,
        }
        async with aiohttp.ClientSession() as session:
            async with session.post(
                f"{_OLLAMA_URL}/api/generate",
                json=payload,
                headers={"Content-Type": "application/json"},
                timeout=aiohttp.ClientTimeout(total=_LLM_TIMEOUT),
            ) as resp:
                if resp.status != 200:
                    body_text = await resp.text()
                    logger.error(f"Ollama suggest error ({resp.status}): {body_text}")
                    raise HTTPException(status_code=502, detail=f"LLM request failed: {resp.status}")
                data = await resp.json()
        suggestion = (data.get("response") or "").strip()
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"suggest_field LLM call failed for plan {plan_id}: {e}")
        raise HTTPException(status_code=502, detail="LLM service unavailable")

    return {"suggestion": suggestion}
