//! Resource handlers — resources/list, resources/read, resources/subscribe,
//! resources/unsubscribe, resources/templates/list

use serde_json::Value;

use crate::content::types::Content;
use crate::managers::subscription::{SubscribeRequest, UnsubscribeRequest};
use crate::protocol::errors::McpError;
use crate::protocol::types::JsonRpcResponse;
use crate::server::core::Server;
use crate::server::handler::{
    RequestContext, error_response, require_initialization, success_response,
};

impl Server {
    /// Handle resources/list request (with pagination support)
    pub(crate) async fn handle_resources_list(&self, ctx: RequestContext) -> JsonRpcResponse {
        if let Err(err) = require_initialization(&ctx) {
            return error_response(ctx.request.id, err.to_jsonrpc());
        }

        // Parse cursor if present
        let cursor = ctx
            .params()
            .and_then(|p| p.get("cursor"))
            .and_then(|c| c.as_str());

        let visibility_ctx = self.visibility_context(&ctx.session);
        let all_resources = self
            .resource_manager
            .list_for_session(&ctx.session, &visibility_ctx);

        // Apply pagination
        let result =
            crate::utils::paginate(&all_resources, cursor, crate::utils::DEFAULT_PAGE_SIZE);

        // Build response (only include nextCursor if present)
        let mut response = serde_json::json!({"resources": result.items});
        if let Some(next) = result.next_cursor {
            response["nextCursor"] = serde_json::Value::String(next);
        }
        success_response(ctx.request.id, response)
    }

    /// Handle resources/templates/list request (with pagination support)
    pub(crate) async fn handle_resources_templates_list(
        &self,
        ctx: RequestContext,
    ) -> JsonRpcResponse {
        if let Err(err) = require_initialization(&ctx) {
            return error_response(ctx.request.id, err.to_jsonrpc());
        }

        // Parse cursor if present
        let cursor = ctx
            .params()
            .and_then(|p| p.get("cursor"))
            .and_then(|c| c.as_str());

        let visibility_ctx = self.visibility_context(&ctx.session);
        let all_templates = self
            .resource_manager
            .list_templates_for_session(&ctx.session, &visibility_ctx);

        // Apply pagination
        let result =
            crate::utils::paginate(&all_templates, cursor, crate::utils::DEFAULT_PAGE_SIZE);

        // Build response (only include nextCursor if present)
        let mut response = serde_json::json!({"resourceTemplates": result.items});
        if let Some(next) = result.next_cursor {
            response["nextCursor"] = serde_json::Value::String(next);
        }
        success_response(ctx.request.id, response)
    }

    /// Handle resources/read request
    pub(crate) async fn handle_resources_read(&self, ctx: RequestContext) -> JsonRpcResponse {
        if let Err(err) = require_initialization(&ctx) {
            return error_response(ctx.request.id, err.to_jsonrpc());
        }

        // Parse resource read params
        let params = ctx.params().cloned().unwrap_or(Value::Null);
        let uri = match params.get("uri").and_then(|v| v.as_str()) {
            Some(uri) => uri,
            None => {
                return error_response(
                    ctx.request.id,
                    McpError::validation("invalid_params", "Missing 'uri' field").to_jsonrpc(),
                );
            }
        };

        // Read resource - ResourceContent already includes uri/mimeType
        let visibility_ctx = self.visibility_context(&ctx.session);
        match self
            .resource_manager
            .read_for_session(
                uri,
                std::collections::HashMap::new(),
                &ctx.session,
                &self.logger,
                &visibility_ctx,
            )
            .await
        {
            Ok(contents) => {
                // ResourceContent.to_value() already produces MCP-compliant JSON with uri/mimeType
                let content_values: Vec<Value> = contents.iter().map(|c| c.to_value()).collect();
                success_response(
                    ctx.request.id,
                    serde_json::json!({"contents": content_values}),
                )
            }
            Err(e) => error_response(
                ctx.request.id,
                McpError::internal("resource_read_failed", e.to_string()).to_jsonrpc(),
            ),
        }
    }

    /// Handle resources/subscribe request
    ///
    /// Subscribes the current session to receive notifications when a resource changes.
    pub(crate) async fn handle_resources_subscribe(&self, ctx: RequestContext) -> JsonRpcResponse {
        if let Err(err) = require_initialization(&ctx) {
            return error_response(ctx.request.id, err.to_jsonrpc());
        }

        // Check if resources.subscribe capability is advertised
        let caps = self.capabilities.load();
        if caps.resources.is_none()
            || caps.resources.as_ref().and_then(|r| r.subscribe) != Some(true)
        {
            return error_response(
                ctx.request.id,
                McpError::validation(
                    "capability_not_supported",
                    "Resource subscriptions are not enabled on this server",
                )
                .to_jsonrpc(),
            );
        }

        // Parse subscribe params
        let params = ctx.params().cloned().unwrap_or(Value::Null);
        let req: SubscribeRequest = match serde_json::from_value(params) {
            Ok(req) => req,
            Err(_) => {
                return error_response(
                    ctx.request.id,
                    McpError::validation("invalid_params", "Missing or invalid 'uri' field")
                        .to_jsonrpc(),
                );
            }
        };

        // Subscribe the session to the resource URI
        self.subscription_manager
            .subscribe(&ctx.session.id, &req.uri);

        tracing::debug!(
            session_id = %ctx.session.id,
            uri = %req.uri,
            "Session subscribed to resource"
        );

        success_response(ctx.request.id, serde_json::json!({}))
    }

    /// Handle resources/unsubscribe request
    pub(crate) async fn handle_resources_unsubscribe(
        &self,
        ctx: RequestContext,
    ) -> JsonRpcResponse {
        if let Err(err) = require_initialization(&ctx) {
            return error_response(ctx.request.id, err.to_jsonrpc());
        }

        // Parse unsubscribe params
        let params = ctx.params().cloned().unwrap_or(Value::Null);
        let req: UnsubscribeRequest = match serde_json::from_value(params) {
            Ok(req) => req,
            Err(_) => {
                return error_response(
                    ctx.request.id,
                    McpError::validation("invalid_params", "Missing or invalid 'uri' field")
                        .to_jsonrpc(),
                );
            }
        };

        // Unsubscribe the session from the resource URI
        self.subscription_manager
            .unsubscribe(&ctx.session.id, &req.uri);

        tracing::debug!(
            session_id = %ctx.session.id,
            uri = %req.uri,
            "Session unsubscribed from resource"
        );

        success_response(ctx.request.id, serde_json::json!({}))
    }
}
