// vapora-mcp-server: Model Context Protocol server for VAPORA v1.0
// Phase 2: Standalone MCP server with HTTP endpoints

use std::net::SocketAddr;

use axum::{
    extract::{Json, Path},
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post},
    Router,
};
use clap::Parser;
use serde::{Deserialize, Serialize};
use serde_json::json;
use tokio::net::TcpListener;
use tracing::{info, warn};

#[derive(Parser)]
#[command(name = "vapora-mcp-server")]
#[command(about = "VAPORA MCP Server - Model Context Protocol for AI Agents", long_about = None)]
struct Args {
    #[arg(short, long, default_value = "3000")]
    port: u16,

    #[arg(short = 'H', long, default_value = "127.0.0.1")]
    host: String,
}

// ============================================================================
// Request/Response Types
// ============================================================================

#[derive(Debug, Deserialize)]
struct InvokeToolRequest {
    tool: String,
    parameters: serde_json::Value,
}

#[derive(Debug, Serialize)]
struct ToolDefinition {
    name: String,
    description: String,
    parameters: serde_json::Value,
}

#[derive(Debug, Serialize)]
struct ResourceDefinition {
    uri: String,
    description: String,
}

#[derive(Debug, Serialize)]
struct PromptDefinition {
    name: String,
    description: String,
}

// ============================================================================
// Handlers
// ============================================================================

async fn health() -> impl IntoResponse {
    Json(json!({
        "status": "healthy",
        "version": env!("CARGO_PKG_VERSION"),
        "service": "vapora-mcp-server"
    }))
}

async fn list_tools() -> impl IntoResponse {
    let tools = vec![
        ToolDefinition {
            name: "kanban_create_task".to_string(),
            description: "Create task in Kanban board".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "project_id": { "type": "string", "description": "Project ID" },
                    "title": { "type": "string", "description": "Task title" },
                    "description": { "type": "string", "description": "Task description" },
                    "priority": {
                        "type": "string",
                        "enum": ["low", "medium", "high", "critical"],
                        "description": "Task priority"
                    }
                },
                "required": ["project_id", "title", "priority"]
            }),
        },
        ToolDefinition {
            name: "kanban_update_task".to_string(),
            description: "Update task status (reorder)".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "task_id": { "type": "string", "description": "Task ID" },
                    "status": {
                        "type": "string",
                        "enum": ["todo", "doing", "review", "done"],
                        "description": "New status"
                    },
                    "order": { "type": "integer", "description": "Order within column" }
                },
                "required": ["task_id", "status"]
            }),
        },
        ToolDefinition {
            name: "get_project_summary".to_string(),
            description: "Get project summary and statistics".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "project_id": { "type": "string", "description": "Project ID" }
                },
                "required": ["project_id"]
            }),
        },
        ToolDefinition {
            name: "list_agents".to_string(),
            description: "List all available agents".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {}
            }),
        },
        ToolDefinition {
            name: "get_agent_capabilities".to_string(),
            description: "Get agent capabilities".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "agent_id": { "type": "string", "description": "Agent ID" }
                },
                "required": ["agent_id"]
            }),
        },
        ToolDefinition {
            name: "assign_task_to_agent".to_string(),
            description: "Assign a task to an agent".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "role": { "type": "string", "description": "Agent role (developer, reviewer, etc.)" },
                    "task_title": { "type": "string", "description": "Task title" },
                    "task_description": { "type": "string", "description": "Task description" }
                },
                "required": ["role", "task_title", "task_description"]
            }),
        },
    ];

    Json(json!({ "tools": tools }))
}

async fn invoke_tool(Json(request): Json<InvokeToolRequest>) -> impl IntoResponse {
    info!("Invoking tool: {}", request.tool);

    let result = match request.tool.as_str() {
        "kanban_create_task" => json!({
            "success": true,
            "task_id": uuid::Uuid::new_v4().to_string(),
            "message": "Task created successfully"
        }),
        "kanban_update_task" => json!({
            "success": true,
            "message": "Task updated successfully"
        }),
        "get_project_summary" => json!({
            "project_id": request.parameters.get("project_id").and_then(|v| v.as_str()).unwrap_or("unknown"),
            "total_tasks": 42,
            "completed": 15,
            "in_progress": 12,
            "blocked": 3,
            "success": true
        }),
        "list_agents" => json!({
            "agents": [
                {"id": "architect-001", "role": "Architect", "status": "Active"},
                {"id": "developer-001", "role": "Developer", "status": "Active"},
                {"id": "reviewer-001", "role": "CodeReviewer", "status": "Active"},
                {"id": "tester-001", "role": "Tester", "status": "Active"},
                {"id": "documenter-001", "role": "Documenter", "status": "Active"},
                {"id": "devops-001", "role": "DevOps", "status": "Active"},
                {"id": "monitor-001", "role": "Monitor", "status": "Active"},
                {"id": "security-001", "role": "Security", "status": "Active"},
            ],
            "success": true
        }),
        "get_agent_capabilities" => json!({
            "agent_id": request.parameters.get("agent_id").and_then(|v| v.as_str()).unwrap_or("unknown"),
            "role": "Developer",
            "capabilities": ["coding", "debugging", "refactoring"],
            "llm_provider": "claude",
            "llm_model": "claude-sonnet-4-5-20250929",
            "success": true
        }),
        "assign_task_to_agent" => json!({
            "task_id": uuid::Uuid::new_v4().to_string(),
            "agent_id": uuid::Uuid::new_v4().to_string(),
            "status": "assigned",
            "success": true
        }),
        _ => {
            warn!("Unknown tool: {}", request.tool);
            json!({
                "error": format!("Unknown tool: {}", request.tool),
                "success": false
            })
        }
    };

    (StatusCode::OK, Json(result))
}

async fn list_resources() -> impl IntoResponse {
    let resources = vec![
        ResourceDefinition {
            uri: "vapora://projects".to_string(),
            description: "Access to all projects".to_string(),
        },
        ResourceDefinition {
            uri: "vapora://tasks".to_string(),
            description: "Access to all tasks".to_string(),
        },
        ResourceDefinition {
            uri: "vapora://agents".to_string(),
            description: "Access to agent registry".to_string(),
        },
        ResourceDefinition {
            uri: "vapora://workflows".to_string(),
            description: "Access to workflow definitions".to_string(),
        },
        ResourceDefinition {
            uri: "vapora://llm-router".to_string(),
            description: "Access to LLM router configuration".to_string(),
        },
    ];

    Json(json!({ "resources": resources }))
}

async fn list_prompts() -> impl IntoResponse {
    let prompts = vec![
        PromptDefinition {
            name: "analyze_task".to_string(),
            description: "Analyze a task and suggest improvements".to_string(),
        },
        PromptDefinition {
            name: "code_review_prompt".to_string(),
            description: "Generate code review feedback".to_string(),
        },
        PromptDefinition {
            name: "architecture_design".to_string(),
            description: "Design system architecture for a feature".to_string(),
        },
        PromptDefinition {
            name: "test_generation".to_string(),
            description: "Generate comprehensive tests for code".to_string(),
        },
    ];

    Json(json!({ "prompts": prompts }))
}

async fn get_resource(Path(resource_uri): Path<String>) -> impl IntoResponse {
    info!("Fetching resource: {}", resource_uri);

    let content = match resource_uri.as_str() {
        "projects" => json!({
            "projects": [
                {"id": "proj-1", "name": "VAPORA v1.0", "status": "active"},
                {"id": "proj-2", "name": "Example Project", "status": "active"}
            ]
        }),
        "agents" => json!({
            "agents": [
                {"id": "architect-001", "role": "Architect"},
                {"id": "developer-001", "role": "Developer"}
            ]
        }),
        _ => json!({
            "error": "Resource not found"
        }),
    };

    Json(content)
}

// ============================================================================
// Main
// ============================================================================

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Initialize tracing
    tracing_subscriber::fmt()
        .with_target(false)
        .compact()
        .init();

    let args = Args::parse();

    // Build router
    let app = Router::new()
        .route("/health", get(health))
        .route("/mcp/tools", get(list_tools))
        .route("/mcp/invoke", post(invoke_tool))
        .route("/mcp/resources", get(list_resources))
        .route("/mcp/resources/:uri", get(get_resource))
        .route("/mcp/prompts", get(list_prompts));

    // Bind address
    let addr = format!("{}:{}", args.host, args.port).parse::<SocketAddr>()?;

    let listener = TcpListener::bind(&addr).await?;

    info!("========================================");
    info!("VAPORA MCP Server v{}", env!("CARGO_PKG_VERSION"));
    info!("========================================");
    info!("Listening on http://{}", addr);
    info!("");
    info!("Endpoints:");
    info!("  GET  /health              - Health check");
    info!("  GET  /mcp/tools           - List available tools");
    info!("  POST /mcp/invoke          - Invoke a tool");
    info!("  GET  /mcp/resources       - List available resources");
    info!("  GET  /mcp/resources/:uri  - Get a specific resource");
    info!("  GET  /mcp/prompts         - List available prompts");
    info!("========================================");

    axum::serve(listener, app).await?;

    Ok(())
}

#[cfg(test)]
mod tests {
    use axum_test::TestServer;

    use super::*;

    #[tokio::test]
    async fn test_health_endpoint() {
        let app = Router::new().route("/health", get(health));
        let server = TestServer::new(app).unwrap();

        let response = server.get("/health").await;
        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert_eq!(body["status"], "healthy");
    }

    #[tokio::test]
    async fn test_list_tools() {
        let app = Router::new().route("/mcp/tools", get(list_tools));
        let server = TestServer::new(app).unwrap();

        let response = server.get("/mcp/tools").await;
        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert!(body["tools"].is_array());
    }

    #[tokio::test]
    async fn test_invoke_tool() {
        let app = Router::new().route("/mcp/invoke", post(invoke_tool));
        let server = TestServer::new(app).unwrap();

        let request = json!({
            "tool": "list_agents",
            "parameters": {}
        });

        let response = server.post("/mcp/invoke").json(&request).await;
        assert_eq!(response.status_code(), StatusCode::OK);

        let body: serde_json::Value = response.json();
        assert_eq!(body["success"], true);
    }
}
