"""
SparkNet Review Dashboard - FastAPI Backend.

Serves the Svelte frontend and provides API endpoints
for the approval queue.
"""

import json
from pathlib import Path
from typing import Optional, List
from fastapi import FastAPI, HTTPException, Request
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel

# Handle imports for both package and direct execution
import sys
_sdk_path = Path(__file__).parent.parent.parent
if str(_sdk_path) not in sys.path:
    sys.path.insert(0, str(_sdk_path))

from sparknet_sdk.approval.queue import ApprovalQueue, ApprovalState, get_approval_queue
from sparknet_sdk.models import Spark, SparkType, KnowledgeType


# === Pydantic Models ===

class SparkCreate(BaseModel):
    """Create a test spark for the queue."""
    content: str
    spark_type: str = "pattern"
    knowledge_type: str = "procedural"
    domain: Optional[str] = None
    confidence: float = 0.8
    tags: List[str] = []


class ApprovalAction(BaseModel):
    """Action on a spark."""
    reason: Optional[str] = None
    override_block: bool = False


class SparkUpdate(BaseModel):
    """Update a spark's content."""
    content: Optional[str] = None
    domain: Optional[str] = None
    tags: Optional[List[str]] = None
    rescan: bool = True


class QueueStats(BaseModel):
    """Queue statistics."""
    pending: int
    approved: int
    rejected: int
    blocked: int
    shared: int
    local_only: int
    total: int
    needs_review: int


# === App Setup ===

app = FastAPI(
    title="SparkNet Review Dashboard",
    description="Security-first approval queue for sharing sparks",
    version="2.0.0",
)

# CORS for development (Vite runs on different port)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Static files directory
STATIC_DIR = Path(__file__).parent / "static"


# === API Routes ===

@app.get("/api/health")
async def health_check():
    """Health check endpoint."""
    return {"status": "healthy", "service": "sparknet-dashboard"}


@app.get("/api/stats")
async def get_stats():
    """Get queue statistics."""
    queue = get_approval_queue()
    stats = queue.get_stats()
    return stats


@app.get("/api/sparks/pending")
async def list_pending(limit: int = 50):
    """List pending sparks awaiting review."""
    queue = get_approval_queue()
    sparks = queue.list_pending(limit=limit)
    return {"sparks": [s.to_dict() for s in sparks], "count": len(sparks)}


@app.get("/api/sparks/blocked")
async def list_blocked(limit: int = 50):
    """List blocked sparks (security issues detected)."""
    queue = get_approval_queue()
    sparks = queue.list_blocked(limit=limit)
    return {"sparks": [s.to_dict() for s in sparks], "count": len(sparks)}


@app.get("/api/sparks/approved")
async def list_approved(limit: int = 50):
    """List approved sparks ready to share."""
    queue = get_approval_queue()
    sparks = queue.list_approved(limit=limit)
    return {"sparks": [s.to_dict() for s in sparks], "count": len(sparks)}


@app.get("/api/sparks/{spark_id}")
async def get_spark(spark_id: str):
    """Get a specific spark by ID."""
    queue = get_approval_queue()
    spark = queue.get(spark_id)
    if not spark:
        raise HTTPException(status_code=404, detail="Spark not found")
    return spark.to_dict()


@app.post("/api/sparks/{spark_id}/approve")
async def approve_spark(spark_id: str, action: ApprovalAction = ApprovalAction()):
    """Approve a spark for sharing."""
    queue = get_approval_queue()
    success = queue.approve(
        spark_id,
        reason=action.reason,
        override_block=action.override_block,
    )
    if not success:
        spark = queue.get(spark_id)
        if not spark:
            raise HTTPException(status_code=404, detail="Spark not found")
        if spark.state == ApprovalState.BLOCKED and not action.override_block:
            raise HTTPException(
                status_code=400,
                detail="Spark is blocked. Set override_block=true to approve anyway."
            )
        raise HTTPException(status_code=400, detail="Cannot approve this spark")
    return {"success": True, "spark_id": spark_id, "action": "approved"}


@app.post("/api/sparks/{spark_id}/reject")
async def reject_spark(spark_id: str, action: ApprovalAction = ApprovalAction()):
    """Reject a spark (will not be shared)."""
    queue = get_approval_queue()
    success = queue.reject(spark_id, reason=action.reason)
    if not success:
        raise HTTPException(status_code=404, detail="Spark not found")
    return {"success": True, "spark_id": spark_id, "action": "rejected"}


@app.post("/api/sparks/{spark_id}/local-only")
async def mark_local_only(spark_id: str):
    """Mark a spark as local-only (will never be shared)."""
    queue = get_approval_queue()
    success = queue.mark_local_only(spark_id)
    if not success:
        raise HTTPException(status_code=404, detail="Spark not found")
    return {"success": True, "spark_id": spark_id, "action": "local_only"}


@app.delete("/api/sparks/{spark_id}")
async def delete_spark(spark_id: str):
    """Permanently delete a spark from the queue."""
    queue = get_approval_queue()
    success = queue.delete(spark_id)
    if not success:
        raise HTTPException(status_code=404, detail="Spark not found")
    return {"success": True, "spark_id": spark_id, "action": "deleted"}


@app.patch("/api/sparks/{spark_id}")
async def update_spark(spark_id: str, update: SparkUpdate):
    """
    Update a spark's content (works even after approval).

    You can edit content, domain, or tags. Security scan re-runs automatically.
    """
    queue = get_approval_queue()
    updated = queue.update(
        spark_id,
        content=update.content,
        domain=update.domain,
        tags=update.tags,
        rescan=update.rescan,
    )
    if not updated:
        raise HTTPException(status_code=404, detail="Spark not found")
    return {
        "success": True,
        "spark_id": spark_id,
        "action": "updated",
        "spark": updated.to_dict(),
    }


@app.post("/api/sparks/{spark_id}/revert")
async def revert_spark(spark_id: str):
    """
    Revert a spark back to pending state for re-review.

    Use this after editing an approved spark to trigger re-approval.
    """
    queue = get_approval_queue()
    success = queue.revert_to_pending(spark_id)
    if not success:
        spark = queue.get(spark_id)
        if not spark:
            raise HTTPException(status_code=404, detail="Spark not found")
        raise HTTPException(
            status_code=400,
            detail=f"Cannot revert spark in state: {spark.state.value}"
        )
    return {"success": True, "spark_id": spark_id, "action": "reverted_to_pending"}


@app.post("/api/sparks/test")
async def create_test_spark(spark_data: SparkCreate):
    """Create a test spark for development/demo purposes."""
    import uuid

    spark = Spark(
        id=str(uuid.uuid4()),
        content=spark_data.content,
        spark_type=SparkType(spark_data.spark_type),
        knowledge_type=KnowledgeType(spark_data.knowledge_type),
        domain=spark_data.domain,
        confidence=spark_data.confidence,
        tags=spark_data.tags,
    )

    queue = get_approval_queue()
    pending = queue.add(spark)

    return {
        "success": True,
        "spark_id": pending.id,
        "state": pending.state.value,
        "has_warnings": pending.has_warnings,
        "warnings": pending.warnings,
    }


# === Static File Serving ===

@app.get("/")
async def serve_index():
    """Serve the main dashboard."""
    index_path = STATIC_DIR / "index.html"
    if index_path.exists():
        return FileResponse(index_path)
    # Development fallback message
    return JSONResponse({
        "message": "Dashboard frontend not built",
        "hint": "Run 'npm run build' in sparknet_sdk/dashboard/frontend/",
        "api_docs": "/docs",
    })


# Mount static files (JS, CSS, assets)
if STATIC_DIR.exists():
    app.mount("/assets", StaticFiles(directory=STATIC_DIR / "assets"), name="assets")


# === Runner ===

def run_dashboard(host: str = "127.0.0.1", port: int = 8502):
    """Run the dashboard server."""
    import uvicorn

    print("=" * 50)
    print("  SparkNet Review Dashboard")
    print("  Security-first approval for sharing")
    print("=" * 50)
    print()
    print(f"  Dashboard: http://{host}:{port}")
    print(f"  API Docs:  http://{host}:{port}/docs")
    print()
    print("  Press Ctrl+C to stop")
    print()

    uvicorn.run(app, host=host, port=port, log_level="info")


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="SparkNet Review Dashboard")
    parser.add_argument("--host", default="127.0.0.1", help="Host to bind to")
    parser.add_argument("--port", type=int, default=8502, help="Port to bind to")
    args = parser.parse_args()

    run_dashboard(host=args.host, port=args.port)
