// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use rialo_shared_types::Subscription;
use serde::{Deserialize, Serialize};
use validator::Validate;

/// Reason why the lineage traversal was truncated
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum TruncationReason {
    /// Traversal stopped due to reaching maximum depth
    MaxDepth,
    /// Traversal stopped due to reaching maximum number of nodes
    MaxNodes,
    /// Traversal was not truncated
    None,
}

/// Request parameters for the `getWorkflowLineage` RPC method
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Validate)]
#[serde(rename_all = "camelCase")]
pub struct GetWorkflowLineageRequest {
    /// The root transaction signature to start traversal from (base58-encoded)
    #[validate(length(min = 1, message = "Signature cannot be empty"))]
    #[validate(custom(function = crate::validation::validate_signature))]
    pub signature: String,

    /// Maximum depth to traverse (default: 3)
    #[validate(range(min = 1, max = 100, message = "Max depth must be between 1 and 100"))]
    #[serde(rename = "maxDepth")]
    pub max_depth: Option<usize>,

    /// Whether to include event details in the response
    #[serde(rename = "includeEvents")]
    pub include_events: Option<bool>,
}

/// Response for the `getWorkflowLineage` RPC method
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetWorkflowLineageResponse {
    /// The lineage graph data
    pub lineage: WorkflowLineage,

    /// List of leaf transaction signatures (nodes with no outgoing edges) (base58-encoded)
    pub leaves: Vec<String>,

    /// Whether the traversal was truncated due to maxDepth
    pub truncated: bool,

    /// The reason for truncation
    pub truncation_reason: TruncationReason,

    /// Hints for continuation - transaction IDs that are good candidates for expanding the tree (base58-encoded)
    /// These are nodes that have subscriptions (potentially unexplored children) but were not
    /// traversed due to limits.
    pub continuation_hints: Vec<String>,
}

/// The lineage tree structure
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowLineage {
    /// All nodes (transactions) in the lineage tree
    pub workflow_nodes: Vec<WorkflowNode>,
}

/// A node in the workflow lineage tree (represents a transaction)
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowNode {
    /// Transaction signature (base58-encoded)
    pub id: String,

    /// Depth from the root transaction
    pub depth: usize,

    /// Transaction metadata
    pub data: TransactionNodeData,

    /// List of subscriptions created by this transaction
    pub subscriptions: Vec<Subscription>,

    /// Information about what triggered this transaction (None for root transaction)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub triggered_by: Option<TriggerInfo>,

    /// List of child transaction IDs (base58-encoded)
    pub workflow_children: Vec<String>,

    /// Whether this node has more children that weren't explored due to traversal limits
    /// This is true when the node has subscriptions but we stopped traversing
    pub has_more_children: bool,
}

/// Transaction metadata for a node
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionNodeData {
    /// Block height where the transaction was processed
    pub block_height: u64,

    /// Unix timestamp when the transaction was processed
    pub timestamp: u64,

    /// Whether the transaction was successful
    pub success: bool,

    /// Number of instructions in the transaction
    pub instruction_count: usize,

    /// List of program IDs (base58-encoded) that were invoked by this transaction's instructions
    pub instruction_program_ids: Vec<String>,
}

/// Timestamp range for filtering
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TimestampRange {
    pub from: u64,
    pub to: u64,
}

/// Information about what triggered a transaction
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TriggerInfo {
    /// The event that triggered this transaction
    pub event: EventData,

    /// The subscription account pubkey that triggered this transaction (base58-encoded)
    pub subscription_pubkey: String,

    /// Optional timestamp range filter that was active
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timestamp_range: Option<TimestampRange>,

    /// Optional event account filter that was active (base58-encoded public key)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub event_account: Option<String>,
}

/// Event data structure  
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EventData {
    /// Event topic
    pub topic: String,

    /// Event attributes
    pub attributes: serde_json::Map<String, serde_json::Value>,
}

#[cfg(test)]
mod tests {
    use rialo_shared_types::SubscriptionKind;
    use serde_json::{from_str, json};

    use super::*;

    #[test]
    fn test_get_workflow_lineage_request_serialization() {
        let request = GetWorkflowLineageRequest {
            signature: "test_signature".to_string(),
            max_depth: Some(5),
            include_events: Some(true),
        };

        let json = serde_json::to_string(&request).unwrap();
        let deserialized: GetWorkflowLineageRequest = from_str(&json).unwrap();

        assert_eq!(deserialized.signature, "test_signature");
        assert_eq!(deserialized.max_depth, Some(5));
        assert_eq!(deserialized.include_events, Some(true));
    }

    #[test]
    fn test_get_workflow_lineage_response_serialization() {
        let node = WorkflowNode {
            id: "tx123".to_string(),
            depth: 0,
            data: TransactionNodeData {
                block_height: 100,
                timestamp: 1234567890,
                success: true,
                instruction_count: 1,
                instruction_program_ids: vec!["11111111111111111111111111111111".to_string()],
            },
            subscriptions: vec![Subscription {
                kind: SubscriptionKind::Persistent,
                topic: "test.event".to_string(),
                instructions: vec![],
                subscriber: "subscriber123".to_string(),
                event_account: Some("event_acc".to_string()),
                timestamp_range: Some((100, 200)),
            }],
            triggered_by: None,
            workflow_children: vec!["child1".to_string()],
            has_more_children: false,
        };

        let response = GetWorkflowLineageResponse {
            lineage: WorkflowLineage {
                workflow_nodes: vec![node],
            },
            leaves: vec!["leaf1".to_string()],
            truncated: false,
            truncation_reason: TruncationReason::None,
            continuation_hints: vec![],
        };

        let json = serde_json::to_string(&response).unwrap();
        let parsed: serde_json::Value = from_str(&json).unwrap();

        assert_eq!(parsed["lineage"]["workflowNodes"][0]["id"], "tx123");
        assert_eq!(parsed["lineage"]["workflowNodes"][0]["depth"], 0);
        assert_eq!(parsed["leaves"][0], "leaf1");
        assert_eq!(parsed["truncated"], false);
    }

    #[test]
    fn test_trigger_info_with_event() {
        let mut attributes = serde_json::Map::new();
        attributes.insert("key".to_string(), json!("value"));

        let trigger_info = TriggerInfo {
            event: EventData {
                topic: "test.topic".to_string(),
                attributes,
            },
            subscription_pubkey: "sub123".to_string(),
            timestamp_range: Some(TimestampRange { from: 100, to: 200 }),
            event_account: Some("event_acc".to_string()),
        };

        let json = serde_json::to_string(&trigger_info).unwrap();
        let parsed: serde_json::Value = from_str(&json).unwrap();

        assert_eq!(parsed["event"]["topic"], "test.topic");
        assert_eq!(parsed["event"]["attributes"]["key"], "value");
        assert_eq!(parsed["subscriptionPubkey"], "sub123");
        assert_eq!(parsed["timestampRange"]["from"], 100);
        assert_eq!(parsed["eventAccount"], "event_acc");
    }

    #[test]
    fn test_optional_fields_serialization() {
        // Test with None values
        let node = WorkflowNode {
            id: "tx123".to_string(),
            depth: 0,
            data: TransactionNodeData {
                block_height: 100,
                timestamp: 1234567890,
                success: true,
                instruction_count: 0,
                instruction_program_ids: vec![],
            },
            subscriptions: vec![],
            triggered_by: None,
            workflow_children: vec![],
            has_more_children: false,
        };

        let json = serde_json::to_string(&node).unwrap();
        let parsed: serde_json::Value = from_str(&json).unwrap();

        // triggeredBy should not be present when None (camelCase due to serde rename)
        assert!(parsed.get("triggeredBy").is_none());
    }
}
