use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::BTreeMap;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KernelNodeType {
    Workspace,
    Folder,
    Page,
    Section,
    Asset,
    Table,
    Book,
    Pdf,
    Mindmap,
    MindmapNode,
    Summary,
    AiNote,
    ReferenceAnchor,
    ContentNode,
    IndexNode,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KernelEdgeType {
    ParentOf,
    ChildOf,
    Contains,
    References,
    BacklinksTo,
    SourceOf,
    DerivedFrom,
    Summarizes,
    Indexes,
    PointsTo,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KernelProjectionKind {
    SidebarTree,
    PageTree,
    FileTree,
    Mindmap,
    ReadView,
    SearchResults,
    RagIndex,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum KernelProjectionCapability {
    Expand,
    Open,
    Drag,
    Drop,
    Select,
    CreateChild,
    Rename,
    Archive,
    Restore,
    ContextMenu,
    Reorder,
    OpenAsset,
    Pick,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KernelProjectionRowKind {
    Document,
    Index,
    Asset,
    AssetFolder,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KernelProjectionResourceKind {
    Workspace,
    Document,
    Index,
    Asset,
    AssetFolder,
    Mindmap,
    Table,
    Book,
    Pdf,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KernelProjectionAssetKind {
    File,
    Mindmap,
    Table,
    Book,
    Pdf,
    Image,
    Video,
    Audio,
    Unknown,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum KernelGraphDirection {
    Outgoing,
    Incoming,
    Both,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct KernelSubtreeRef {
    pub root_node_id: String,
    #[serde(default)]
    pub path: Vec<String>,
    pub depth: Option<u32>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct KernelNodeMetadata {
    pub title: Option<String>,
    pub icon: Option<String>,
    #[serde(default)]
    pub tags: Vec<String>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
    #[serde(default)]
    pub extra: BTreeMap<String, Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelContentPayload {
    pub format: String,
    pub body: Value,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct KernelRefsPayload {
    #[serde(default)]
    pub reference_node_ids: Vec<String>,
    #[serde(default)]
    pub evidence_node_ids: Vec<String>,
    #[serde(default)]
    pub source_node_ids: Vec<String>,
    #[serde(default)]
    pub extra: BTreeMap<String, Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct KernelAuditStamp {
    pub version: u64,
    pub revision: Option<u64>,
    pub request_id: Option<String>,
    pub trace_id: Option<String>,
    pub actor_id: Option<String>,
}

impl Default for KernelAuditStamp {
    fn default() -> Self {
        Self {
            version: 1,
            revision: None,
            request_id: None,
            trace_id: None,
            actor_id: None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelNode {
    pub id: String,
    pub node_type: KernelNodeType,
    pub workspace_id: Option<String>,
    pub parent_id: Option<String>,
    pub subtree: Option<KernelSubtreeRef>,
    pub metadata: KernelNodeMetadata,
    pub content: Option<KernelContentPayload>,
    pub refs: Option<KernelRefsPayload>,
    #[serde(default)]
    pub audit: KernelAuditStamp,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelEdge {
    pub id: String,
    pub edge_type: KernelEdgeType,
    pub workspace_id: Option<String>,
    pub from_node_id: String,
    pub to_node_id: String,
    #[serde(default)]
    pub metadata: BTreeMap<String, Value>,
    #[serde(default)]
    pub audit: KernelAuditStamp,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct KernelProjectionFilter {
    #[serde(default)]
    pub node_types: Vec<KernelNodeType>,
    #[serde(default)]
    pub edge_types: Vec<KernelEdgeType>,
    #[serde(default)]
    pub include_deleted: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct KernelGetNode {
    pub node_id: String,
    pub workspace_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelGetSubtree {
    pub subtree: KernelSubtreeRef,
    pub workspace_id: Option<String>,
    #[serde(default)]
    pub include_edges: bool,
    #[serde(default)]
    pub filters: KernelProjectionFilter,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelListChildren {
    pub parent_node_id: String,
    pub workspace_id: Option<String>,
    #[serde(default)]
    pub node_types: Vec<KernelNodeType>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelListEdges {
    pub node_id: String,
    pub workspace_id: Option<String>,
    #[serde(default)]
    pub edge_types: Vec<KernelEdgeType>,
    pub direction: Option<KernelGraphDirection>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelTraverseGraph {
    pub start_node_id: String,
    pub workspace_id: Option<String>,
    #[serde(default)]
    pub edge_types: Vec<KernelEdgeType>,
    pub max_depth: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelProjectionRequest {
    pub projection: KernelProjectionKind,
    pub workspace_id: Option<String>,
    pub root_node_id: Option<String>,
    pub subtree: Option<KernelSubtreeRef>,
    #[serde(default)]
    pub filters: KernelProjectionFilter,
    #[serde(default)]
    pub include_content: bool,
    #[serde(default)]
    pub include_edges: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelSubtreeResult {
    pub root_node_id: String,
    pub nodes: Vec<KernelNode>,
    pub edges: Vec<KernelEdge>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelEdgeListResult {
    pub node_id: String,
    pub edges: Vec<KernelEdge>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct KernelGraphVisit {
    pub node_id: String,
    pub depth: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelGraphTraversalResult {
    pub start_node_id: String,
    pub visited: Vec<KernelGraphVisit>,
    pub edges: Vec<KernelEdge>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelProjectionItem {
    pub row_id: String,
    pub row_kind: KernelProjectionRowKind,
    pub node_id: String,
    pub parent_node_id: Option<String>,
    pub node_type: KernelNodeType,
    pub projection_kind: KernelProjectionKind,
    pub title: Option<String>,
    pub depth: u32,
    pub position: Option<i64>,
    pub child_count: u32,
    pub expandable: bool,
    pub expanded_by_default: bool,
    #[serde(default)]
    pub capabilities: Vec<KernelProjectionCapability>,
    pub resource_meta: Option<KernelProjectionResourceMeta>,
    pub icon_hint: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct KernelProjectionResourceMeta {
    pub resource_kind: Option<KernelProjectionResourceKind>,
    pub document_id: Option<String>,
    pub asset_id: Option<String>,
    pub workspace_id: Option<String>,
    pub asset_kind: Option<KernelProjectionAssetKind>,
    pub icon_hint: Option<String>,
    #[serde(default)]
    pub extra: BTreeMap<String, Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelProjectionResult {
    pub projection_id: String,
    pub projection: KernelProjectionKind,
    pub root_node_id: Option<String>,
    pub items: Vec<KernelProjectionItem>,
    pub edges: Vec<KernelEdge>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DocumentReadNodeType {
    Page,
    Section,
    ContentNode,
    ReferenceAnchor,
    Mindmap,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DocumentReadNodeMeta {
    pub title: Option<String>,
    pub text_snippet: Option<String>,
    pub block_type: Option<String>,
    pub heading_level: Option<u32>,
    pub numbering: Option<String>,
    pub child_count: u32,
    pub order: u32,
    #[serde(default)]
    pub path: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DocumentReadNode {
    pub id: String,
    pub parent_node_id: Option<String>,
    pub node_type: DocumentReadNodeType,
    pub block_id: Option<String>,
    pub anchor_block_id: Option<String>,
    pub depth: u32,
    pub metadata: DocumentReadNodeMeta,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DocumentReadSubtree {
    pub root_node_id: String,
    pub nodes: Vec<DocumentReadNode>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DocumentReadOutlineEntry {
    pub id: String,
    pub node_id: String,
    pub anchor_block_id: Option<String>,
    pub title: String,
    pub level: u32,
    pub numbering: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DocumentReadEvidenceKind {
    Page,
    Heading,
    Paragraph,
    List,
    Todo,
    Quote,
    Code,
    Media,
    Reference,
    Table,
    Mindmap,
    Text,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DocumentReadEvidenceItem {
    pub id: String,
    pub node_id: String,
    pub block_id: Option<String>,
    pub kind: DocumentReadEvidenceKind,
    pub snippet: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct DocumentReadStats {
    pub block_count: u32,
    pub heading_count: u32,
    pub evidence_count: u32,
    pub max_depth: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DocumentReadPageSubtree {
    pub projection_id: String,
    pub projection: String,
    pub root_node_id: String,
    pub root_node: DocumentReadNode,
    pub subtree: DocumentReadSubtree,
    #[serde(default)]
    pub outline: Vec<DocumentReadOutlineEntry>,
    #[serde(default)]
    pub evidence: Vec<DocumentReadEvidenceItem>,
    pub stats: DocumentReadStats,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DocumentContentResult {
    pub content: Value,
    pub revision: u64,
    pub conflict_detection_key: String,
    pub title: Option<String>,
    pub page_subtree: DocumentReadPageSubtree,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelCreateNode {
    pub node: KernelNode,
    pub position: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelUpdateNode {
    pub node_id: String,
    pub metadata: Option<KernelNodeMetadata>,
    pub content: Option<KernelContentPayload>,
    pub refs: Option<KernelRefsPayload>,
    pub audit: Option<KernelAuditStamp>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct KernelMoveSubtree {
    pub subtree: KernelSubtreeRef,
    pub new_parent_node_id: Option<String>,
    pub sort_order: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelAttachEdge {
    pub edge: KernelEdge,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct KernelDetachEdge {
    pub edge_id: Option<String>,
    pub from_node_id: Option<String>,
    pub to_node_id: Option<String>,
    pub edge_type: Option<KernelEdgeType>,
}

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

    #[test]
    fn kernel_projection_request_roundtrip_keeps_subtree_and_filters() {
        let request = KernelProjectionRequest {
            projection: KernelProjectionKind::SidebarTree,
            workspace_id: Some("ws_1".into()),
            root_node_id: Some("page_root".into()),
            subtree: Some(KernelSubtreeRef {
                root_node_id: "page_root".into(),
                path: vec!["page_root".into(), "page_child".into()],
                depth: Some(2),
            }),
            filters: KernelProjectionFilter {
                node_types: vec![KernelNodeType::Page, KernelNodeType::Folder],
                edge_types: vec![KernelEdgeType::ParentOf],
                include_deleted: false,
            },
            include_content: false,
            include_edges: true,
        };

        let value = serde_json::to_value(&request).expect("request 应可序列化");
        assert_eq!(value["projection"], json!("sidebar_tree"));
        assert_eq!(value["subtree"]["rootNodeId"], json!("page_root"));

        let decoded: KernelProjectionRequest =
            serde_json::from_value(value).expect("request 应可反序列化");
        assert_eq!(decoded, request);
    }

    #[test]
    fn kernel_node_serializes_content_refs_and_audit_boundaries() {
        let node = KernelNode {
            id: "node_1".into(),
            node_type: KernelNodeType::Summary,
            workspace_id: Some("ws_1".into()),
            parent_id: Some("page_1".into()),
            subtree: Some(KernelSubtreeRef {
                root_node_id: "page_1".into(),
                path: vec!["page_1".into(), "node_1".into()],
                depth: Some(1),
            }),
            metadata: KernelNodeMetadata {
                title: Some("摘要节点".into()),
                icon: None,
                tags: vec!["summary".into()],
                created_at: Some("2026-04-16T00:00:00Z".into()),
                updated_at: Some("2026-04-16T00:00:00Z".into()),
                extra: BTreeMap::new(),
            },
            content: Some(KernelContentPayload {
                format: "markdown".into(),
                body: json!({"text": "摘要正文"}),
            }),
            refs: Some(KernelRefsPayload {
                reference_node_ids: vec!["page_1".into()],
                evidence_node_ids: vec!["asset_1".into()],
                source_node_ids: vec!["pdf_1".into()],
                extra: BTreeMap::new(),
            }),
            audit: KernelAuditStamp {
                version: 3,
                revision: Some(9),
                request_id: Some("req_1".into()),
                trace_id: Some("trace_1".into()),
                actor_id: Some("user_1".into()),
            },
        };

        let value = serde_json::to_value(&node).expect("node 应可序列化");
        assert_eq!(value["nodeType"], json!("summary"));
        assert_eq!(value["content"]["format"], json!("markdown"));
        assert_eq!(value["refs"]["referenceNodeIds"], json!(["page_1"]));
        assert_eq!(value["audit"]["traceId"], json!("trace_1"));
    }

    #[test]
    fn kernel_projection_item_serializes_contract_fields() {
        let item = KernelProjectionItem {
            row_id: "asset:page_1".into(),
            row_kind: KernelProjectionRowKind::Asset,
            node_id: "page_1".into(),
            parent_node_id: None,
            node_type: KernelNodeType::Page,
            projection_kind: KernelProjectionKind::FileTree,
            title: Some("页面 1".into()),
            depth: 0,
            position: Some(1),
            child_count: 2,
            expandable: true,
            expanded_by_default: true,
            capabilities: vec![
                KernelProjectionCapability::Expand,
                KernelProjectionCapability::Open,
                KernelProjectionCapability::CreateChild,
            ],
            resource_meta: Some(KernelProjectionResourceMeta {
                resource_kind: Some(KernelProjectionResourceKind::Document),
                document_id: Some("page_1".into()),
                asset_id: None,
                workspace_id: Some("ws_1".into()),
                asset_kind: Some(KernelProjectionAssetKind::File),
                icon_hint: Some("page".into()),
                extra: BTreeMap::new(),
            }),
            icon_hint: Some("page".into()),
        };

        let value = serde_json::to_value(&item).expect("projection item 应可序列化");
        assert_eq!(value["rowId"], json!("asset:page_1"));
        assert_eq!(value["rowKind"], json!("asset"));
        assert_eq!(value["projectionKind"], json!("file_tree"));
        assert_eq!(
            value["capabilities"],
            json!(["expand", "open", "create-child"])
        );
        assert_eq!(value["resourceMeta"]["resourceKind"], json!("document"));
        assert_eq!(value["iconHint"], json!("page"));
    }
}
