"""
Context Engineering API Routes

Provides endpoints for:
- Viewing playbook contents
- Managing session state
- Recording feedback on playbook items
- Triggering compaction
- Monitoring cache efficiency
- Exporting learning reports

Part of the ACE (Agentic Context Engineering) implementation for Atomizer.
"""

from fastapi import APIRouter, HTTPException, Query
from pathlib import Path
from typing import Optional, List
from pydantic import BaseModel
from datetime import datetime
import sys

# Add parent paths for imports
sys.path.append(str(Path(__file__).parent.parent.parent.parent.parent))

router = APIRouter()

# Paths
ATOMIZER_ROOT = Path(__file__).parents[4]
PLAYBOOK_PATH = ATOMIZER_ROOT / "knowledge_base" / "playbook.json"


# Pydantic models for request/response
class PlaybookItemResponse(BaseModel):
    id: str
    category: str
    content: str
    helpful_count: int
    harmful_count: int
    net_score: int
    confidence: float
    tags: List[str]
    created_at: str
    last_used: Optional[str]


class PlaybookSummary(BaseModel):
    total_items: int
    by_category: dict
    version: int
    last_updated: str
    avg_score: float
    top_score: int
    lowest_score: int


class FeedbackRequest(BaseModel):
    item_id: str
    helpful: bool


class InsightRequest(BaseModel):
    category: str
    content: str
    tags: Optional[List[str]] = None
    source_trial: Optional[int] = None


class SessionStateResponse(BaseModel):
    session_id: str
    task_type: Optional[str]
    study_name: Optional[str]
    study_status: str
    trials_completed: int
    trials_total: int
    best_value: Optional[float]
    recent_actions: List[str]
    recent_errors: List[str]


# Helper function to get playbook
def get_playbook():
    """Load playbook, handling import errors gracefully."""
    try:
        from optimization_engine.context.playbook import AtomizerPlaybook
        return AtomizerPlaybook.load(PLAYBOOK_PATH)
    except ImportError as e:
        raise HTTPException(
            status_code=500,
            detail=f"Context engineering module not available: {str(e)}"
        )


# Playbook endpoints
@router.get("/playbook", response_model=PlaybookSummary)
async def get_playbook_summary():
    """Get playbook summary statistics."""
    playbook = get_playbook()
    stats = playbook.get_stats()

    return PlaybookSummary(
        total_items=stats["total_items"],
        by_category=stats["by_category"],
        version=stats["version"],
        last_updated=stats["last_updated"],
        avg_score=stats["avg_score"],
        top_score=stats["max_score"],
        lowest_score=stats["min_score"]
    )


@router.get("/playbook/items", response_model=List[PlaybookItemResponse])
async def get_playbook_items(
    category: Optional[str] = Query(None, description="Filter by category (str, mis, tool, etc.)"),
    min_score: int = Query(0, description="Minimum net score"),
    min_confidence: float = Query(0.0, description="Minimum confidence (0.0-1.0)"),
    limit: int = Query(50, description="Maximum items to return"),
    offset: int = Query(0, description="Pagination offset")
):
    """
    Get playbook items with optional filtering.

    Categories:
    - str: Strategy
    - mis: Mistake
    - tool: Tool usage
    - cal: Calculation
    - dom: Domain knowledge
    - wf: Workflow
    """
    playbook = get_playbook()

    items = list(playbook.items.values())

    # Filter by category
    if category:
        try:
            from optimization_engine.context.playbook import InsightCategory
            cat = InsightCategory(category)
            items = [i for i in items if i.category == cat]
        except ValueError:
            raise HTTPException(400, f"Invalid category: {category}. Valid: str, mis, tool, cal, dom, wf")

    # Filter by score
    items = [i for i in items if i.net_score >= min_score]

    # Filter by confidence
    items = [i for i in items if i.confidence >= min_confidence]

    # Sort by score
    items.sort(key=lambda x: x.net_score, reverse=True)

    # Paginate
    items = items[offset:offset + limit]

    return [
        PlaybookItemResponse(
            id=item.id,
            category=item.category.value,
            content=item.content,
            helpful_count=item.helpful_count,
            harmful_count=item.harmful_count,
            net_score=item.net_score,
            confidence=item.confidence,
            tags=item.tags,
            created_at=item.created_at,
            last_used=item.last_used
        )
        for item in items
    ]


@router.get("/playbook/items/{item_id}", response_model=PlaybookItemResponse)
async def get_playbook_item(item_id: str):
    """Get a specific playbook item by ID."""
    playbook = get_playbook()

    if item_id not in playbook.items:
        raise HTTPException(404, f"Item not found: {item_id}")

    item = playbook.items[item_id]

    return PlaybookItemResponse(
        id=item.id,
        category=item.category.value,
        content=item.content,
        helpful_count=item.helpful_count,
        harmful_count=item.harmful_count,
        net_score=item.net_score,
        confidence=item.confidence,
        tags=item.tags,
        created_at=item.created_at,
        last_used=item.last_used
    )


@router.post("/playbook/feedback")
async def record_feedback(request: FeedbackRequest):
    """
    Record feedback on a playbook item.

    This is how the system learns:
    - helpful=true increases the item's score
    - helpful=false decreases the item's score
    """
    playbook = get_playbook()

    if request.item_id not in playbook.items:
        raise HTTPException(404, f"Item not found: {request.item_id}")

    playbook.record_outcome(request.item_id, helpful=request.helpful)
    playbook.save(PLAYBOOK_PATH)

    item = playbook.items[request.item_id]

    return {
        "item_id": request.item_id,
        "new_score": item.net_score,
        "new_confidence": item.confidence,
        "helpful_count": item.helpful_count,
        "harmful_count": item.harmful_count
    }


@router.post("/playbook/insights")
async def add_insight(request: InsightRequest):
    """
    Add a new insight to the playbook.

    Categories:
    - str: Strategy - Optimization strategies that work
    - mis: Mistake - Common mistakes to avoid
    - tool: Tool - Tool usage patterns
    - cal: Calculation - Formulas and calculations
    - dom: Domain - Domain-specific knowledge (FEA, NX)
    - wf: Workflow - Workflow patterns
    """
    try:
        from optimization_engine.context.playbook import InsightCategory
    except ImportError as e:
        raise HTTPException(500, f"Context module not available: {e}")

    # Validate category
    try:
        category = InsightCategory(request.category)
    except ValueError:
        raise HTTPException(400, f"Invalid category: {request.category}")

    playbook = get_playbook()

    item = playbook.add_insight(
        category=category,
        content=request.content,
        source_trial=request.source_trial,
        tags=request.tags
    )

    playbook.save(PLAYBOOK_PATH)

    return {
        "item_id": item.id,
        "category": item.category.value,
        "content": item.content,
        "message": "Insight added successfully"
    }


@router.delete("/playbook/items/{item_id}")
async def delete_playbook_item(item_id: str):
    """Delete a playbook item."""
    playbook = get_playbook()

    if item_id not in playbook.items:
        raise HTTPException(404, f"Item not found: {item_id}")

    content = playbook.items[item_id].content[:50]
    del playbook.items[item_id]
    playbook.save(PLAYBOOK_PATH)

    return {
        "deleted": item_id,
        "content_preview": content
    }


@router.post("/playbook/prune")
async def prune_playbook(threshold: int = Query(-3, description="Net score threshold for pruning")):
    """
    Prune harmful items from the playbook.

    Items with net_score <= threshold will be removed.
    """
    playbook = get_playbook()

    removed_count = playbook.prune_harmful(threshold=threshold)
    playbook.save(PLAYBOOK_PATH)

    return {
        "items_pruned": removed_count,
        "threshold_used": threshold,
        "remaining_items": len(playbook.items)
    }


@router.get("/playbook/context")
async def get_playbook_context(
    task_type: str = Query("optimization", description="Task type for context filtering"),
    max_items: int = Query(15, description="Maximum items to include"),
    min_confidence: float = Query(0.5, description="Minimum confidence threshold")
):
    """
    Get playbook context string formatted for LLM consumption.

    This is what gets injected into the LLM context window.
    """
    playbook = get_playbook()

    context = playbook.get_context_for_task(
        task_type=task_type,
        max_items=max_items,
        min_confidence=min_confidence
    )

    return {
        "context": context,
        "items_included": min(max_items, len(playbook.items)),
        "task_type": task_type
    }


# Session state endpoints
@router.get("/session", response_model=SessionStateResponse)
async def get_session_state():
    """Get current session state."""
    try:
        from optimization_engine.context.session_state import get_session
        session = get_session()

        return SessionStateResponse(
            session_id=session.session_id,
            task_type=session.exposed.task_type.value if session.exposed.task_type else None,
            study_name=session.exposed.study_name,
            study_status=session.exposed.study_status,
            trials_completed=session.exposed.trials_completed,
            trials_total=session.exposed.trials_total,
            best_value=session.exposed.best_value,
            recent_actions=session.exposed.recent_actions[-10:],
            recent_errors=session.exposed.recent_errors[-5:]
        )
    except ImportError:
        raise HTTPException(500, "Session state module not available")


@router.get("/session/context")
async def get_session_context():
    """Get session context string for LLM consumption."""
    try:
        from optimization_engine.context.session_state import get_session
        session = get_session()

        return {
            "context": session.get_llm_context(),
            "session_id": session.session_id,
            "last_updated": session.last_updated
        }
    except ImportError:
        raise HTTPException(500, "Session state module not available")


# Cache monitoring endpoints
@router.get("/cache/stats")
async def get_cache_stats():
    """Get KV-cache efficiency statistics."""
    try:
        from optimization_engine.context.cache_monitor import get_cache_optimizer
        optimizer = get_cache_optimizer()

        return {
            "stats": optimizer.get_stats_dict(),
            "report": optimizer.get_report()
        }
    except ImportError:
        return {
            "message": "Cache monitoring not active",
            "stats": None
        }


# Learning report endpoints
@router.get("/learning/report")
async def get_learning_report():
    """Get a comprehensive learning report."""
    playbook = get_playbook()
    stats = playbook.get_stats()

    # Get top and worst performers
    items = list(playbook.items.values())
    items.sort(key=lambda x: x.net_score, reverse=True)

    top_performers = [
        {"id": i.id, "content": i.content[:100], "score": i.net_score}
        for i in items[:10]
    ]

    items.sort(key=lambda x: x.net_score)
    worst_performers = [
        {"id": i.id, "content": i.content[:100], "score": i.net_score}
        for i in items[:5] if i.net_score < 0
    ]

    return {
        "generated_at": datetime.now().isoformat(),
        "playbook_stats": stats,
        "top_performers": top_performers,
        "worst_performers": worst_performers,
        "recommendations": _generate_recommendations(playbook)
    }


def _generate_recommendations(playbook) -> List[str]:
    """Generate recommendations based on playbook state."""
    recommendations = []

    # Check for harmful items
    harmful = [i for i in playbook.items.values() if i.net_score < -3]
    if harmful:
        recommendations.append(
            f"Consider pruning {len(harmful)} harmful items (net_score < -3)"
        )

    # Check for untested items
    untested = [
        i for i in playbook.items.values()
        if i.helpful_count + i.harmful_count == 0
    ]
    if len(untested) > 10:
        recommendations.append(
            f"{len(untested)} items have no feedback - consider testing them"
        )

    # Check category balance
    stats = playbook.get_stats()
    if stats["by_category"].get("MISTAKE", 0) < 5:
        recommendations.append(
            "Low mistake count - actively record errors when they occur"
        )

    if not recommendations:
        recommendations.append("Playbook is in good health!")

    return recommendations
