//! Compute integration for distributed VM execution.
//!
//! This module enables VM contract execution to be distributed across
//! heterogeneous compute resources (CPU, GPU, TPU, NPU, etc.).

#[cfg(feature = "compute")]
use synor_compute::{
    processor::{Operation, OperationType, Precision, ProcessorId, ProcessorType},
    scheduler::BalancingStrategy,
    task::{Task, TaskId, TaskPriority, TaskStatus},
    ComputeCluster, ComputeJob, JobId, NodeId,
};


/// Configuration for compute-accelerated VM execution.
#[derive(Clone, Debug)]
pub struct ComputeConfig {
    /// Whether to enable compute offloading.
    pub enabled: bool,
    /// Minimum gas threshold to consider offloading.
    pub min_gas_threshold: u64,
    /// Preferred balancing strategy.
    pub strategy: String,
    /// Maximum parallel tasks.
    pub max_parallel_tasks: usize,
}

impl Default for ComputeConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            min_gas_threshold: 100_000,
            strategy: "balanced".to_string(),
            max_parallel_tasks: 8,
        }
    }
}

/// Operations that can be offloaded to compute nodes.
#[derive(Clone, Debug)]
pub enum OffloadableOp {
    /// Memory operations (bulk copy, fill).
    Memory { size: usize },
    /// Cryptographic hashing.
    Hash { algorithm: String, size: usize },
    /// Merkle tree operations.
    MerkleProof { depth: usize, leaves: usize },
    /// Signature verification batch.
    VerifySignatures { count: usize },
    /// Storage proof verification.
    VerifyStorageProof { size: usize },
    /// Custom compute (e.g., ZK proof generation).
    Custom { name: String, flops: f64, memory: u64 },
}

impl OffloadableOp {
    /// Estimate gas cost for this operation.
    pub fn estimated_gas(&self) -> u64 {
        match self {
            OffloadableOp::Memory { size } => (*size as u64) / 100,
            OffloadableOp::Hash { size, .. } => (*size as u64) / 50,
            OffloadableOp::MerkleProof { depth, leaves } => {
                (*depth as u64) * (*leaves as u64) * 10
            }
            OffloadableOp::VerifySignatures { count } => (*count as u64) * 5000,
            OffloadableOp::VerifyStorageProof { size } => (*size as u64) * 100,
            OffloadableOp::Custom { flops, .. } => (*flops as u64) / 1_000_000,
        }
    }

    /// Check if this operation should be offloaded based on gas cost.
    pub fn should_offload(&self, config: &ComputeConfig) -> bool {
        config.enabled && self.estimated_gas() >= config.min_gas_threshold
    }
}

/// Result of compute offload.
#[derive(Clone, Debug)]
#[derive(Default)]
pub struct ComputeResult {
    /// Whether the operation was offloaded.
    pub offloaded: bool,
    /// Processor type used (if offloaded).
    pub processor_type: Option<String>,
    /// Gas savings from offloading.
    pub gas_savings: u64,
    /// Actual execution time (microseconds).
    pub execution_time_us: u64,
}


/// Compute-aware execution context.
#[cfg(feature = "compute")]
pub struct ComputeContext {
    /// Compute cluster connection.
    cluster: Option<Arc<ComputeCluster>>,
    /// Configuration.
    config: ComputeConfig,
    /// Pending tasks.
    pending_tasks: Vec<TaskId>,
}

#[cfg(feature = "compute")]
impl ComputeContext {
    /// Create a new compute context.
    pub fn new(config: ComputeConfig) -> Self {
        Self {
            cluster: None,
            config,
            pending_tasks: Vec::new(),
        }
    }

    /// Connect to a compute cluster.
    pub fn connect(&mut self, cluster: Arc<ComputeCluster>) {
        self.cluster = Some(cluster);
    }

    /// Offload an operation to the compute cluster.
    pub fn offload(&mut self, op: OffloadableOp) -> ComputeResult {
        if !op.should_offload(&self.config) {
            return ComputeResult::default();
        }

        let cluster = match &self.cluster {
            Some(c) => c,
            None => return ComputeResult::default(),
        };

        // Map offloadable op to compute operation
        let (operation, priority) = match &op {
            OffloadableOp::VerifySignatures { count } => (
                Operation::Generic {
                    op_type: OperationType::MatMul, // Use matmul for batch verification
                    flops: (*count as f64) * 10_000.0,
                    memory: (*count as u64) * 96, // 96 bytes per signature
                },
                TaskPriority::High,
            ),
            OffloadableOp::Hash { size, .. } => (
                Operation::Generic {
                    op_type: OperationType::Sum, // Reduction-like operation
                    flops: (*size as f64) * 10.0,
                    memory: *size as u64,
                },
                TaskPriority::Normal,
            ),
            OffloadableOp::MerkleProof { depth, leaves } => (
                Operation::Generic {
                    op_type: OperationType::Sum,
                    flops: (*depth as f64) * (*leaves as f64) * 100.0,
                    memory: (*leaves as u64) * 32,
                },
                TaskPriority::Normal,
            ),
            OffloadableOp::Custom { flops, memory, .. } => (
                Operation::Generic {
                    op_type: OperationType::MatMul,
                    flops: *flops,
                    memory: *memory,
                },
                TaskPriority::Normal,
            ),
            _ => return ComputeResult::default(),
        };

        // Create and submit task
        let task = Task {
            id: TaskId::new(),
            operation,
            priority,
            dependencies: vec![],
            status: TaskStatus::Pending,
            deadline: None,
        };

        self.pending_tasks.push(task.id);

        ComputeResult {
            offloaded: true,
            processor_type: Some("cpu".to_string()), // Will be determined by scheduler
            gas_savings: op.estimated_gas() / 2, // 50% gas savings for offloaded ops
            execution_time_us: 0,
        }
    }

    /// Get number of pending tasks.
    pub fn pending_count(&self) -> usize {
        self.pending_tasks.len()
    }

    /// Wait for all pending tasks to complete.
    pub async fn wait_all(&mut self) {
        self.pending_tasks.clear();
    }
}

/// Stub implementation when compute feature is disabled.
#[cfg(not(feature = "compute"))]
pub struct ComputeContext {
    config: ComputeConfig,
}

#[cfg(not(feature = "compute"))]
impl ComputeContext {
    pub fn new(config: ComputeConfig) -> Self {
        Self { config }
    }

    pub fn offload(&mut self, _op: OffloadableOp) -> ComputeResult {
        ComputeResult::default()
    }

    pub fn pending_count(&self) -> usize {
        0
    }

    pub async fn wait_all(&mut self) {}
}

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

    #[test]
    fn test_offloadable_op_gas() {
        let sig_verify = OffloadableOp::VerifySignatures { count: 100 };
        assert_eq!(sig_verify.estimated_gas(), 500_000);

        let hash = OffloadableOp::Hash {
            algorithm: "blake3".to_string(),
            size: 1_000_000,
        };
        assert_eq!(hash.estimated_gas(), 20_000);
    }

    #[test]
    fn test_should_offload() {
        let config = ComputeConfig {
            enabled: true,
            min_gas_threshold: 100_000,
            ..Default::default()
        };

        let small_op = OffloadableOp::Hash {
            algorithm: "blake3".to_string(),
            size: 1000,
        };
        assert!(!small_op.should_offload(&config));

        let large_op = OffloadableOp::VerifySignatures { count: 100 };
        assert!(large_op.should_offload(&config));
    }

    #[test]
    fn test_compute_context_disabled() {
        let config = ComputeConfig {
            enabled: false,
            ..Default::default()
        };

        let mut ctx = ComputeContext::new(config);
        let op = OffloadableOp::VerifySignatures { count: 100 };
        let result = ctx.offload(op);

        assert!(!result.offloaded);
    }
}
