# ==============================================
# Breakpilot Drive - Unit API Routes
# ==============================================
# Endpoints for listing/getting definitions, sessions, telemetry,
# recommendations, and analytics.
# CRUD definition routes are in unit_definition_routes.py.
# Extracted from unit_api.py for file-size compliance.

from fastapi import APIRouter, HTTPException, Query, Depends, Request
from typing import List, Optional, Dict, Any
from datetime import datetime, timedelta
import uuid
import logging

from .models import (
    UnitDefinitionResponse,
    CreateSessionRequest,
    SessionResponse,
    TelemetryPayload,
    TelemetryResponse,
    CompleteSessionRequest,
    SessionSummaryResponse,
    UnitListItem,
    RecommendedUnit,
)
from .helpers import (
    get_optional_current_user,
    get_unit_database,
    create_session_token,
    get_session_from_token,
    REQUIRE_AUTH,
)

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/units", tags=["Breakpilot Units"])


# ==============================================
# Definition List/Get Endpoints
# ==============================================

@router.get("/definitions", response_model=List[UnitListItem])
async def list_unit_definitions(
    template: Optional[str] = Query(None, description="Filter by template: flight_path, station_loop"),
    grade: Optional[str] = Query(None, description="Filter by grade level"),
    locale: str = Query("de-DE", description="Filter by locale"),
    user: Optional[Dict[str, Any]] = Depends(get_optional_current_user)
) -> List[UnitListItem]:
    """
    List available unit definitions.

    Returns published units matching the filter criteria.
    """
    db = await get_unit_database()
    if db:
        try:
            units = await db.list_units(
                template=template,
                grade=grade,
                locale=locale,
                published_only=True
            )
            return [
                UnitListItem(
                    unit_id=u["unit_id"],
                    template=u["template"],
                    difficulty=u["difficulty"],
                    duration_minutes=u["duration_minutes"],
                    locale=u["locale"],
                    grade_band=u["grade_band"],
                )
                for u in units
            ]
        except Exception as e:
            logger.error(f"Failed to list units: {e}")

    # Fallback: return demo unit
    return [
        UnitListItem(
            unit_id="demo_unit_v1",
            template="flight_path",
            difficulty="base",
            duration_minutes=5,
            locale=["de-DE"],
            grade_band=["5", "6", "7"],
        )
    ]


@router.get("/definitions/{unit_id}", response_model=UnitDefinitionResponse)
async def get_unit_definition(
    unit_id: str,
    user: Optional[Dict[str, Any]] = Depends(get_optional_current_user)
) -> UnitDefinitionResponse:
    """
    Get a specific unit definition.

    Returns the full unit configuration including stops, interactions, etc.
    """
    db = await get_unit_database()
    if db:
        try:
            unit = await db.get_unit_definition(unit_id)
            if unit:
                return UnitDefinitionResponse(
                    unit_id=unit["unit_id"],
                    template=unit["template"],
                    version=unit["version"],
                    locale=unit["locale"],
                    grade_band=unit["grade_band"],
                    duration_minutes=unit["duration_minutes"],
                    difficulty=unit["difficulty"],
                    definition=unit["definition"],
                )
        except Exception as e:
            logger.error(f"Failed to get unit definition: {e}")

    # Demo unit fallback
    if unit_id == "demo_unit_v1":
        return UnitDefinitionResponse(
            unit_id="demo_unit_v1",
            template="flight_path",
            version="1.0.0",
            locale=["de-DE"],
            grade_band=["5", "6", "7"],
            duration_minutes=5,
            difficulty="base",
            definition={
                "unit_id": "demo_unit_v1",
                "template": "flight_path",
                "version": "1.0.0",
                "learning_objectives": ["Demo: Grundfunktion testen"],
                "stops": [
                    {"stop_id": "stop_1", "label": {"de-DE": "Start"}, "interaction": {"type": "aim_and_pass"}},
                    {"stop_id": "stop_2", "label": {"de-DE": "Mitte"}, "interaction": {"type": "aim_and_pass"}},
                    {"stop_id": "stop_3", "label": {"de-DE": "Ende"}, "interaction": {"type": "aim_and_pass"}},
                ],
                "teacher_controls": {"allow_skip": True, "allow_replay": True},
            },
        )

    raise HTTPException(status_code=404, detail=f"Unit not found: {unit_id}")


# ==============================================
# Session Endpoints
# ==============================================

@router.post("/sessions", response_model=SessionResponse)
async def create_unit_session(
    request_data: CreateSessionRequest,
    request: Request,
    user: Optional[Dict[str, Any]] = Depends(get_optional_current_user)
) -> SessionResponse:
    """
    Create a new unit session.

    - Validates unit exists
    - Creates session record
    - Returns session token for telemetry
    """
    session_id = str(uuid.uuid4())
    expires_at = datetime.utcnow() + timedelta(hours=4)

    # Validate unit exists
    db = await get_unit_database()
    if db:
        try:
            unit = await db.get_unit_definition(request_data.unit_id)
            if not unit:
                raise HTTPException(status_code=404, detail=f"Unit not found: {request_data.unit_id}")

            # Create session in database
            total_stops = len(unit.get("definition", {}).get("stops", []))
            await db.create_session(
                session_id=session_id,
                unit_id=request_data.unit_id,
                student_id=request_data.student_id,
                locale=request_data.locale,
                difficulty=request_data.difficulty,
                total_stops=total_stops,
            )
        except HTTPException:
            raise
        except Exception as e:
            logger.error(f"Failed to create session: {e}")
            # Continue with in-memory fallback

    # Create session token
    session_token = create_session_token(session_id, request_data.student_id)

    # Build definition URL
    base_url = str(request.base_url).rstrip("/")
    definition_url = f"{base_url}/api/units/definitions/{request_data.unit_id}"

    return SessionResponse(
        session_id=session_id,
        unit_definition_url=definition_url,
        session_token=session_token,
        telemetry_endpoint="/api/units/telemetry",
        expires_at=expires_at,
    )


@router.post("/telemetry", response_model=TelemetryResponse)
async def receive_telemetry(
    payload: TelemetryPayload,
    request: Request,
) -> TelemetryResponse:
    """
    Receive batched telemetry events from Unity client.

    - Validates session token
    - Stores events in database
    - Returns count of accepted events
    """
    # Verify session token
    session_data = await get_session_from_token(request)
    if session_data is None:
        # Allow without auth in dev mode
        if REQUIRE_AUTH:
            raise HTTPException(status_code=401, detail="Invalid or expired session token")
        logger.warning("Telemetry received without valid token (dev mode)")

    # Verify session_id matches
    if session_data and session_data.get("session_id") != payload.session_id:
        raise HTTPException(status_code=403, detail="Session ID mismatch")

    accepted = 0
    db = await get_unit_database()

    for event in payload.events:
        try:
            # Set timestamp if not provided
            timestamp = event.ts or datetime.utcnow().isoformat()

            if db:
                await db.store_telemetry_event(
                    session_id=payload.session_id,
                    event_type=event.type,
                    stop_id=event.stop_id,
                    timestamp=timestamp,
                    metrics=event.metrics,
                )

            accepted += 1
            logger.debug(f"Telemetry: {event.type} for session {payload.session_id}")

        except Exception as e:
            logger.error(f"Failed to store telemetry event: {e}")

    return TelemetryResponse(accepted=accepted)


@router.post("/sessions/{session_id}/complete", response_model=SessionSummaryResponse)
async def complete_session(
    session_id: str,
    request_data: CompleteSessionRequest,
    request: Request,
) -> SessionSummaryResponse:
    """
    Complete a unit session.

    - Processes postcheck answers if provided
    - Calculates learning gain
    - Returns summary and recommendations
    """
    # Verify session token
    session_data = await get_session_from_token(request)
    if REQUIRE_AUTH and session_data is None:
        raise HTTPException(status_code=401, detail="Invalid or expired session token")

    db = await get_unit_database()
    summary = {}
    recommendations = {}

    if db:
        try:
            # Get session data
            session = await db.get_session(session_id)
            if not session:
                raise HTTPException(status_code=404, detail="Session not found")

            # Calculate postcheck score if answers provided
            postcheck_score = None
            if request_data.postcheck_answers:
                # Simple scoring: count correct answers
                # In production, would validate against question bank
                postcheck_score = len(request_data.postcheck_answers) * 0.2  # Placeholder
                postcheck_score = min(postcheck_score, 1.0)

            # Complete session in database
            await db.complete_session(
                session_id=session_id,
                postcheck_score=postcheck_score,
            )

            # Get updated session summary
            session = await db.get_session(session_id)

            # Calculate learning gain
            pre_score = session.get("precheck_score")
            post_score = session.get("postcheck_score")
            learning_gain = None
            if pre_score is not None and post_score is not None:
                learning_gain = post_score - pre_score

            summary = {
                "session_id": session_id,
                "unit_id": session.get("unit_id"),
                "duration_seconds": session.get("duration_seconds"),
                "completion_rate": session.get("completion_rate"),
                "precheck_score": pre_score,
                "postcheck_score": post_score,
                "pre_to_post_gain": learning_gain,
                "stops_completed": session.get("stops_completed"),
                "total_stops": session.get("total_stops"),
            }

            # Get recommendations
            recommendations = await db.get_recommendations(
                student_id=session.get("student_id"),
                completed_unit_id=session.get("unit_id"),
            )

        except HTTPException:
            raise
        except Exception as e:
            logger.error(f"Failed to complete session: {e}")
            summary = {"session_id": session_id, "error": str(e)}

    else:
        # Fallback summary
        summary = {
            "session_id": session_id,
            "duration_seconds": 0,
            "completion_rate": 1.0,
            "message": "Database not available",
        }

    return SessionSummaryResponse(
        summary=summary,
        next_recommendations=recommendations or {
            "h5p_activity_ids": [],
            "worksheet_pdf_url": None,
        },
    )


@router.get("/sessions/{session_id}")
async def get_session(
    session_id: str,
    request: Request,
) -> Dict[str, Any]:
    """
    Get session details.

    Returns current state of a session including progress.
    """
    # Verify session token
    session_data = await get_session_from_token(request)
    if REQUIRE_AUTH and session_data is None:
        raise HTTPException(status_code=401, detail="Invalid or expired session token")

    db = await get_unit_database()
    if db:
        try:
            session = await db.get_session(session_id)
            if session:
                return session
        except Exception as e:
            logger.error(f"Failed to get session: {e}")

    raise HTTPException(status_code=404, detail="Session not found")


# ==============================================
# Recommendations & Analytics
# ==============================================

@router.get("/recommendations/{student_id}", response_model=List[RecommendedUnit])
async def get_recommendations(
    student_id: str,
    grade: Optional[str] = Query(None, description="Grade level filter"),
    locale: str = Query("de-DE", description="Locale filter"),
    limit: int = Query(5, ge=1, le=20),
    user: Optional[Dict[str, Any]] = Depends(get_optional_current_user)
) -> List[RecommendedUnit]:
    """
    Get recommended units for a student.

    Based on completion status and performance.
    """
    db = await get_unit_database()
    if db:
        try:
            recommendations = await db.get_student_recommendations(
                student_id=student_id,
                grade=grade,
                locale=locale,
                limit=limit,
            )
            return [
                RecommendedUnit(
                    unit_id=r["unit_id"],
                    template=r["template"],
                    difficulty=r["difficulty"],
                    reason=r["reason"],
                )
                for r in recommendations
            ]
        except Exception as e:
            logger.error(f"Failed to get recommendations: {e}")

    # Fallback: recommend demo unit
    return [
        RecommendedUnit(
            unit_id="demo_unit_v1",
            template="flight_path",
            difficulty="base",
            reason="Neu: Noch nicht gespielt",
        )
    ]


@router.get("/analytics/student/{student_id}")
async def get_student_analytics(
    student_id: str,
    user: Optional[Dict[str, Any]] = Depends(get_optional_current_user)
) -> Dict[str, Any]:
    """
    Get unit analytics for a student.

    Includes completion rates, learning gains, time spent.
    """
    db = await get_unit_database()
    if db:
        try:
            analytics = await db.get_student_unit_analytics(student_id)
            return analytics
        except Exception as e:
            logger.error(f"Failed to get analytics: {e}")

    return {
        "student_id": student_id,
        "units_attempted": 0,
        "units_completed": 0,
        "avg_completion_rate": 0.0,
        "avg_learning_gain": None,
        "total_minutes": 0,
    }


@router.get("/analytics/unit/{unit_id}")
async def get_unit_analytics(
    unit_id: str,
    user: Optional[Dict[str, Any]] = Depends(get_optional_current_user)
) -> Dict[str, Any]:
    """
    Get analytics for a specific unit.

    Shows aggregate performance across all students.
    """
    db = await get_unit_database()
    if db:
        try:
            analytics = await db.get_unit_performance(unit_id)
            return analytics
        except Exception as e:
            logger.error(f"Failed to get unit analytics: {e}")

    return {
        "unit_id": unit_id,
        "total_sessions": 0,
        "completed_sessions": 0,
        "completion_percent": 0.0,
        "avg_duration_minutes": 0,
        "avg_learning_gain": None,
    }


@router.get("/health")
async def health_check() -> Dict[str, Any]:
    """Health check for unit API."""
    db = await get_unit_database()
    db_status = "connected" if db else "disconnected"

    return {
        "status": "healthy",
        "service": "breakpilot-units",
        "database": db_status,
        "auth_required": REQUIRE_AUTH,
    }
