use crate::app::AppState;
use crate::context::RequestContext;
use crate::error::WebError;
use crate::hermes_tools::{doc, ToolCallInput};
use serde_json::{json, Value};

pub async fn context_snapshot(
    _state: &AppState,
    _context: &RequestContext,
    input: &ToolCallInput,
) -> Result<Value, WebError> {
    let context_refs = input
        .arg_value("contextRefs")
        .or_else(|| input.arg_value("context_refs"))
        .unwrap_or_else(|| json!([]));
    Ok(json!({
        "ok": true,
        "schema": "mnote.context_snapshot.v1",
        "runId": input.run_id,
        "sessionId": input.session_id,
        "workspace": {
            "workspaceId": input.effective_workspace_id(),
            "sourceKind": input.effective_source_kind(),
            "rootUri": input.effective_root_uri()
        },
        "contextRefs": context_refs,
        "primaryTarget": {
            "documentId": input.effective_document_id(),
            "fileVersion": input.arg_value("fileVersion").or_else(|| input.arg_value("file_version"))
        },
        "availableReads": {
            "currentPage": context_ref_enabled(input, "current_page"),
            "selection": context_ref_enabled(input, "selection"),
            "folder": context_ref_enabled(input, "folder"),
            "changedFiles": context_ref_enabled(input, "changed_files")
        }
    }))
}

pub async fn resolve_target(
    _state: &AppState,
    _context: &RequestContext,
    input: &ToolCallInput,
) -> Result<Value, WebError> {
    Ok(json!({
        "ok": true,
        "schema": "mnote.context_target.v1",
        "workspaceId": input.effective_workspace_id(),
        "documentId": input.effective_document_id(),
        "sourceKind": input.effective_source_kind(),
        "rootUri": input.effective_root_uri(),
        "relativePath": input.arg_string("relativePath").or_else(|| input.arg_string("relative_path")),
        "fileVersion": input.arg_value("fileVersion").or_else(|| input.arg_value("file_version")),
        "contextRefs": input.arg_value("contextRefs").or_else(|| input.arg_value("context_refs")).unwrap_or_else(|| json!([]))
    }))
}

pub async fn read_current_page(
    state: &AppState,
    context: &RequestContext,
    input: &ToolCallInput,
) -> Result<Value, WebError> {
    if !context_ref_enabled(input, "current_page") {
        return Err(WebError::new(
            axum::http::StatusCode::FORBIDDEN,
            "mnote_context_current_page_not_allowed",
            "本次 run 未授权 current_page 上下文",
        )
        .with_context(context));
    }
    doc::doc_fetch(state, context, input).await
}

fn context_ref_enabled(input: &ToolCallInput, expected: &str) -> bool {
    input
        .arg_value("contextRefs")
        .or_else(|| input.arg_value("context_refs"))
        .and_then(|value| value.as_array().cloned())
        .map(|items| {
            items.iter().any(|item| {
                item.as_str() == Some(expected)
                    || item.get("kind").and_then(Value::as_str).map(str::trim) == Some(expected)
            })
        })
        .unwrap_or(false)
}

#[cfg(test)]
mod tests {
    use super::*;

    fn input_with_refs(context_refs: Value) -> ToolCallInput {
        ToolCallInput {
            tool_name: "mnote.context.read_current_page".into(),
            workspace_id: Some("ws_1".into()),
            document_id: Some("doc_1".into()),
            source_kind: Some("local_folder".into()),
            root_uri: Some("file:///tmp/mnote".into()),
            actor_id: Some("user_1".into()),
            profile: None,
            session_id: Some("sess_1".into()),
            run_id: Some("run_1".into()),
            tool_call_id: Some("tool_1".into()),
            trace_id: Some("trace_1".into()),
            idempotency_key: None,
            dry_run: None,
            capability_scope: Some(vec!["context.read".into()]),
            args: Some(json!({ "contextRefs": context_refs })),
        }
    }

    #[test]
    fn context_ref_enabled_accepts_string_and_object_refs() {
        assert!(context_ref_enabled(
            &input_with_refs(json!(["current_page"])),
            "current_page"
        ));
        assert!(context_ref_enabled(
            &input_with_refs(json!([{ "kind": "current_page" }])),
            "current_page"
        ));
        assert!(!context_ref_enabled(
            &input_with_refs(json!(["selection"])),
            "current_page"
        ));
    }
}
