"""
Session Management API
Handles voice session lifecycle

Endpoints:
- POST   /api/v1/sessions              # Session erstellen
- GET    /api/v1/sessions/{id}         # Session Status
- DELETE /api/v1/sessions/{id}         # Session beenden
- GET    /api/v1/sessions/{id}/tasks   # Pending Tasks
"""
import structlog
from fastapi import APIRouter, HTTPException, Request, Depends
from typing import List, Optional
from datetime import datetime, timedelta

from config import settings
from models.session import (
    VoiceSession,
    SessionCreate,
    SessionResponse,
    SessionStatus,
)
from models.task import TaskResponse, TaskState

logger = structlog.get_logger(__name__)

router = APIRouter()


# In-memory session store (will be replaced with Valkey in production)
# This is transient - sessions are never persisted to disk
_sessions: dict[str, VoiceSession] = {}


async def get_session(session_id: str) -> VoiceSession:
    """Get session by ID or raise 404."""
    session = _sessions.get(session_id)
    if not session:
        raise HTTPException(status_code=404, detail="Session not found")
    return session


@router.post("", response_model=SessionResponse)
async def create_session(request: Request, session_data: SessionCreate):
    """
    Create a new voice session.

    Returns a session ID and WebSocket URL for audio streaming.
    The client must connect to the WebSocket within 30 seconds.
    """
    logger.info(
        "Creating voice session",
        namespace_id=session_data.namespace_id[:8] + "...",
        device_type=session_data.device_type,
    )

    # Verify namespace key hash
    orchestrator = request.app.state.orchestrator
    encryption = request.app.state.encryption

    if settings.encryption_enabled:
        if not encryption.verify_key_hash(session_data.key_hash):
            logger.warning("Invalid key hash", namespace_id=session_data.namespace_id[:8])
            raise HTTPException(status_code=401, detail="Invalid encryption key hash")

    # Check rate limits
    namespace_sessions = [
        s for s in _sessions.values()
        if s.namespace_id == session_data.namespace_id
        and s.status not in [SessionStatus.CLOSED, SessionStatus.ERROR]
    ]
    if len(namespace_sessions) >= settings.max_sessions_per_user:
        raise HTTPException(
            status_code=429,
            detail=f"Maximum {settings.max_sessions_per_user} concurrent sessions allowed"
        )

    # Create session
    session = VoiceSession(
        namespace_id=session_data.namespace_id,
        key_hash=session_data.key_hash,
        device_type=session_data.device_type,
        client_version=session_data.client_version,
    )

    # Store session (in RAM only)
    _sessions[session.id] = session

    logger.info(
        "Voice session created",
        session_id=session.id[:8],
        namespace_id=session_data.namespace_id[:8],
    )

    # Build WebSocket URL
    # Use X-Forwarded-Proto if behind a reverse proxy (nginx), otherwise use request scheme
    forwarded_proto = request.headers.get("x-forwarded-proto", request.url.scheme)
    host = request.headers.get("host", f"localhost:{settings.port}")
    ws_scheme = "wss" if forwarded_proto == "https" else "ws"
    ws_url = f"{ws_scheme}://{host}/ws/voice?session_id={session.id}"

    return SessionResponse(
        id=session.id,
        namespace_id=session.namespace_id,
        status=session.status,
        created_at=session.created_at,
        websocket_url=ws_url,
    )


@router.get("/{session_id}", response_model=SessionResponse)
async def get_session_status(session_id: str, request: Request):
    """
    Get session status.

    Returns current session state including message count and pending tasks.
    """
    session = await get_session(session_id)

    # Check if session expired
    session_age = datetime.utcnow() - session.created_at
    if session_age > timedelta(hours=settings.session_ttl_hours):
        session.status = SessionStatus.CLOSED
        logger.info("Session expired", session_id=session_id[:8])

    # Build WebSocket URL
    # Use X-Forwarded-Proto if behind a reverse proxy (nginx), otherwise use request scheme
    forwarded_proto = request.headers.get("x-forwarded-proto", request.url.scheme)
    host = request.headers.get("host", f"localhost:{settings.port}")
    ws_scheme = "wss" if forwarded_proto == "https" else "ws"
    ws_url = f"{ws_scheme}://{host}/ws/voice?session_id={session.id}"

    return SessionResponse(
        id=session.id,
        namespace_id=session.namespace_id,
        status=session.status,
        created_at=session.created_at,
        websocket_url=ws_url,
    )


@router.delete("/{session_id}")
async def close_session(session_id: str):
    """
    Close and delete a session.

    All transient data (messages, audio state) is discarded.
    This is the expected cleanup path.
    """
    session = await get_session(session_id)

    logger.info(
        "Closing session",
        session_id=session_id[:8],
        messages_count=len(session.messages),
        tasks_count=len(session.pending_tasks),
    )

    # Mark as closed
    session.status = SessionStatus.CLOSED

    # Remove from active sessions
    del _sessions[session_id]

    return {"status": "closed", "session_id": session_id}


@router.get("/{session_id}/tasks", response_model=List[TaskResponse])
async def get_session_tasks(session_id: str, request: Request, state: Optional[TaskState] = None):
    """
    Get tasks for a session.

    Optionally filter by task state.
    """
    session = await get_session(session_id)

    # Get tasks from the in-memory task store
    from api.tasks import _tasks

    # Filter tasks by session_id and optionally by state
    tasks = [
        task for task in _tasks.values()
        if task.session_id == session_id
        and (state is None or task.state == state)
    ]

    return [
        TaskResponse(
            id=task.id,
            session_id=task.session_id,
            type=task.type,
            state=task.state,
            created_at=task.created_at,
            updated_at=task.updated_at,
            result_available=task.result_ref is not None,
            error_message=task.error_message,
        )
        for task in tasks
    ]


@router.get("/{session_id}/stats")
async def get_session_stats(session_id: str):
    """
    Get session statistics (for debugging/monitoring).

    No PII is returned - only aggregate counts.
    """
    session = await get_session(session_id)

    return {
        "session_id_truncated": session_id[:8],
        "status": session.status.value,
        "age_seconds": (datetime.utcnow() - session.created_at).total_seconds(),
        "message_count": len(session.messages),
        "pending_tasks_count": len(session.pending_tasks),
        "audio_chunks_received": session.audio_chunks_received,
        "audio_chunks_processed": session.audio_chunks_processed,
        "device_type": session.device_type,
    }
