use crate::app::AppConfig;
use crate::context::RequestContext;
use crate::error::WebError;
use crate::routes::query_support::{
    execute_runtime_query_against_data, fetch_query_data_via_convex,
};
use bridge_runtime::RuntimeQueryEnvelopeWire;
use core_protocol::{KernelNodeType, KernelProjectionKind};
use serde_json::{json, Value};

#[derive(Debug, Clone)]
pub struct ProjectionSnapshotSpec<'a> {
    pub workspace_id: &'a str,
    pub root_node_id: Option<&'a str>,
    pub depth: Option<u32>,
    pub projection: KernelProjectionKind,
}

#[derive(Debug, Clone)]
pub struct ProjectionSnapshot {
    pub dataset: Value,
    pub projection: Value,
}

pub fn sidebar_dataset_query(workspace_id: &str) -> RuntimeQueryEnvelopeWire {
    RuntimeQueryEnvelopeWire {
        name: "sidebar.dataset.list".into(),
        payload: json!({
            "workspaceId": workspace_id,
        }),
    }
}

pub async fn load_sidebar_dataset(
    config: &AppConfig,
    context: &RequestContext,
    workspace_id: &str,
) -> Result<Value, WebError> {
    fetch_query_data_via_convex(
        config,
        context,
        Some(workspace_id),
        sidebar_dataset_query(workspace_id),
    )
    .await
}

pub fn projection_query(spec: &ProjectionSnapshotSpec<'_>) -> RuntimeQueryEnvelopeWire {
    RuntimeQueryEnvelopeWire {
        name: "kernel.project_view".into(),
        payload: json!({
            "projection": spec.projection,
            "workspaceId": spec.workspace_id,
            "rootNodeId": spec.root_node_id,
            "depth": spec.depth,
            "includeEdges": true,
            "includeContent": false,
            "nodeTypes": [KernelNodeType::Page],
        }),
    }
}

pub fn subtree_query(
    workspace_id: &str,
    root_node_id: &str,
    depth: Option<u32>,
) -> RuntimeQueryEnvelopeWire {
    RuntimeQueryEnvelopeWire {
        name: "kernel.subtree.get".into(),
        payload: json!({
            "workspaceId": workspace_id,
            "rootNodeId": root_node_id,
            "depth": depth,
            "includeEdges": true,
            "nodeTypes": [KernelNodeType::Page],
        }),
    }
}

pub fn execute_kernel_query(
    context: &RequestContext,
    workspace_id: &str,
    query: RuntimeQueryEnvelopeWire,
    dataset: Value,
) -> Result<Value, WebError> {
    execute_runtime_query_against_data(context, Some(workspace_id), query, dataset)
}

pub async fn load_projection_snapshot(
    config: &AppConfig,
    context: &RequestContext,
    spec: &ProjectionSnapshotSpec<'_>,
) -> Result<ProjectionSnapshot, WebError> {
    let dataset = load_sidebar_dataset(config, context, spec.workspace_id).await?;
    let projection = execute_kernel_query(
        context,
        spec.workspace_id,
        projection_query(spec),
        dataset.clone(),
    )?;

    Ok(ProjectionSnapshot {
        dataset,
        projection,
    })
}
