use crate::app::AppConfig;
use crate::context::RequestContext;
use crate::error::WebError;
use crate::routes::query_support::{
    execute_runtime_query_via_convex, resolve_effective_workspace_id,
};
use crate::routes::snapshot_support::{
    execute_kernel_query, load_projection_snapshot, load_sidebar_dataset, subtree_query,
    ProjectionSnapshotSpec,
};
use bridge_runtime::RuntimeQueryEnvelopeWire;
use core_protocol::KernelProjectionKind;
use serde::Deserialize;
use serde_json::{json, Value};

#[derive(Debug, Clone, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct StreamSnapshotQuery {
    pub workspace_id: Option<String>,
    pub root_node_id: Option<String>,
    pub depth: Option<u32>,
    pub cursor: Option<String>,
    pub limit: Option<u32>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StreamSnapshotScope {
    Workspace,
    Subtree,
}

impl StreamSnapshotScope {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Workspace => "workspace",
            Self::Subtree => "subtree",
        }
    }
}

pub fn resolve_stream_scope(query: &StreamSnapshotQuery) -> StreamSnapshotScope {
    if query
        .root_node_id
        .as_deref()
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .is_some()
    {
        StreamSnapshotScope::Subtree
    } else {
        StreamSnapshotScope::Workspace
    }
}

fn workspace_overview_query(
    workspace_id: &str,
    query: &StreamSnapshotQuery,
) -> RuntimeQueryEnvelopeWire {
    RuntimeQueryEnvelopeWire {
        name: "bridge.workspace.overview".into(),
        payload: json!({
            "workspaceId": workspace_id,
            "cursor": query.cursor,
            "limit": query.limit.unwrap_or(20),
            "commandStatus": Value::Null,
            "eventStatus": Value::Null,
            "targetPageId": Value::Null,
            "targetBlockId": Value::Null,
            "aggregateType": Value::Null,
            "aggregateId": query.root_node_id,
        }),
    }
}

pub async fn load_stream_snapshot(
    config: &AppConfig,
    context: &RequestContext,
    query: &StreamSnapshotQuery,
) -> Result<Value, WebError> {
    let effective_workspace_id =
        resolve_effective_workspace_id(context, query.workspace_id.as_deref(), true)?
            .expect("workspace_required 已确保存在");
    let scope = resolve_stream_scope(query);

    let snapshot = match scope {
        StreamSnapshotScope::Workspace => {
            let loaded = load_projection_snapshot(
                config,
                context,
                &ProjectionSnapshotSpec {
                    workspace_id: &effective_workspace_id,
                    root_node_id: None,
                    depth: query.depth,
                    projection: KernelProjectionKind::SidebarTree,
                },
            )
            .await?;

            json!({
                "dataset": loaded.dataset,
                "tree": loaded.projection,
            })
        }
        StreamSnapshotScope::Subtree => {
            let root_node_id = query
                .root_node_id
                .as_deref()
                .map(str::trim)
                .filter(|value| !value.is_empty())
                .expect("subtree scope 已确保 rootNodeId 存在");
            let dataset = load_sidebar_dataset(config, context, &effective_workspace_id).await?;
            let tree = execute_kernel_query(
                context,
                &effective_workspace_id,
                subtree_query(&effective_workspace_id, root_node_id, query.depth),
                dataset.clone(),
            )?;

            json!({
                "dataset": dataset,
                "tree": tree,
            })
        }
    };

    let overview = execute_runtime_query_via_convex(
        config,
        context,
        Some(&effective_workspace_id),
        workspace_overview_query(&effective_workspace_id, query),
    )
    .await
    .ok();

    Ok(json!({
        "kind": "snapshot",
        "scope": scope.as_str(),
        "requestId": context.trace.request_id,
        "traceId": context.trace.trace_id,
        "workspaceId": effective_workspace_id,
        "rootNodeId": query.root_node_id,
        "depth": query.depth,
        "snapshot": snapshot,
        "overview": overview,
    }))
}

#[cfg(test)]
mod tests {
    use super::{resolve_stream_scope, StreamSnapshotQuery, StreamSnapshotScope};

    #[test]
    fn stream_scope_defaults_to_workspace() {
        assert_eq!(
            resolve_stream_scope(&StreamSnapshotQuery::default()),
            StreamSnapshotScope::Workspace
        );
    }

    #[test]
    fn stream_scope_switches_to_subtree_when_root_exists() {
        assert_eq!(
            resolve_stream_scope(&StreamSnapshotQuery {
                root_node_id: Some("page_root".into()),
                ..StreamSnapshotQuery::default()
            }),
            StreamSnapshotScope::Subtree
        );
    }
}
