//! # strand-errors
//!
//! Standardized error types for the Strand ecosystem.
//!
//! This library provides comprehensive error handling for all Strand-related
//! operations, including storage, cryptography, protocol handling, ownership
//! validation, replication, and general validation errors.
//!
//! ## Example
//!
//! ```rust
//! use strand_errors::{Result, StrandError, StorageError};
//!
//! fn read_block(index: u64) -> Result<Vec<u8>> {
//!     Err(StorageError::BlockNotFound(index).into())
//! }
//! ```

use std::io;
use thiserror::Error;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// Storage-related errors
#[derive(Debug, Error)]
pub enum StorageError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("Block not found: index {0}")]
    BlockNotFound(u64),

    #[error("Corrupted block at index {index}: {reason}")]
    CorruptedBlock { index: u64, reason: String },

    #[error("Storage backend unavailable: {0}")]
    BackendUnavailable(String),

    #[error("Insufficient space: need {required} bytes, have {available}")]
    InsufficientSpace { required: u64, available: u64 },

    #[error("Invalid storage path: {0}")]
    InvalidPath(String),
}

/// Cryptographic operation errors
#[derive(Debug, Error)]
pub enum CryptoError {
    #[error("Invalid signature")]
    InvalidSignature,

    #[error("Invalid public key: {0}")]
    InvalidPublicKey(String),

    #[error("Invalid secret key")]
    InvalidSecretKey,

    #[error("Key derivation failed: {0}")]
    KeyDerivationFailed(String),

    #[error("Encryption failed: {0}")]
    EncryptionFailed(String),

    #[error("Decryption failed: {0}")]
    DecryptionFailed(String),

    #[error("Invalid key length: expected {expected}, got {actual}")]
    InvalidKeyLength { expected: usize, actual: usize },
}

/// Protocol-level errors
#[derive(Debug, Error)]
pub enum ProtocolError {
    #[error("Version mismatch: local {local}, remote {remote}")]
    VersionMismatch { local: u32, remote: u32 },

    #[error("Invalid message type: {0}")]
    InvalidMessageType(u8),

    #[error("Protocol timeout after {0} seconds")]
    Timeout(u64),

    #[error("Invalid state transition: from {from} to {to}")]
    InvalidStateTransition { from: String, to: String },

    #[error("Handshake failed: {0}")]
    HandshakeFailed(String),

    #[error("Channel closed unexpectedly")]
    ChannelClosed,

    #[error("Message too large: {size} bytes (max: {max})")]
    MessageTooLarge { size: usize, max: usize },
}

/// Ownership and permission errors (identity-system agnostic)
#[derive(Debug, Error)]
pub enum OwnershipError {
    #[error("Invalid agent identity: {0}")]
    InvalidAgentIdentity(String),

    #[error("Invalid organization identity: {0}")]
    InvalidOrganizationIdentity(String),

    #[error("Agent {agent_identity} not authorized for action: {action}")]
    AgentNotAuthorized {
        agent_identity: String,
        action: String,
    },

    #[error("Agent {agent_identity} not part of organization {org_identity}")]
    AgentNotInOrganization {
        agent_identity: String,
        org_identity: String,
    },

    #[error("Writer mismatch: expected {expected}, got {actual}")]
    WriterMismatch { expected: String, actual: String },

    #[error("Organization verification failed: {0}")]
    OrganizationVerificationFailed(String),

    #[error("Multi-signatory consensus failed: {obtained}/{required} signatures")]
    MultiSignatoryConsensusFailed { obtained: usize, required: usize },

    #[error("Governance approval required for: {action}")]
    GovernanceApprovalRequired { action: String },

    #[error("Permission denied: {identity} cannot perform {permission}")]
    PermissionDenied {
        identity: String,
        permission: String,
    },

    #[error("Identity resolution failed: {0}")]
    IdentityResolutionFailed(String),

    #[error("Invalid signature for identity: {identity}")]
    InvalidSignatureForIdentity { identity: String },
}

/// Replication and synchronization errors
#[derive(Debug, Error)]
pub enum ReplicationError {
    #[error("Sync failed: {0}")]
    SyncFailed(String),

    #[error("Fork detected at index {0}")]
    ForkDetected(u64),

    #[error("Invalid proof: {0}")]
    InvalidProof(String),

    #[error("Peer disconnected during replication")]
    PeerDisconnected,

    #[error("Bandwidth limit exceeded: {used}/{limit} bytes")]
    BandwidthExceeded { used: u64, limit: u64 },
}

/// Data validation errors
#[derive(Debug, Error)]
pub enum ValidationError {
    #[error("Invalid block size: {0} bytes")]
    InvalidBlockSize(usize),

    #[error("Invalid tree state: {0}")]
    InvalidTreeState(String),

    #[error("Checksum mismatch")]
    ChecksumMismatch,

    #[error("Invalid index: {0}")]
    InvalidIndex(u64),

    #[error("Schema validation failed: {0}")]
    SchemaValidation(String),
}

/// Master error type that encompasses all error categories
#[derive(Debug, Error)]
pub enum StrandError {
    #[error(transparent)]
    Storage(#[from] StorageError),

    #[error(transparent)]
    Crypto(#[from] CryptoError),

    #[error(transparent)]
    Protocol(#[from] ProtocolError),

    #[error(transparent)]
    Ownership(#[from] OwnershipError),

    #[error(transparent)]
    Replication(#[from] ReplicationError),

    #[error(transparent)]
    Validation(#[from] ValidationError),

    #[error("Internal error: {0}")]
    Internal(String),

    #[error("Not implemented: {0}")]
    NotImplemented(String),
}

/// Convenience type alias for Results using StrandError
pub type Result<T> = std::result::Result<T, StrandError>;

/// Provides additional context for errors
pub trait ErrorContext<T> {
    /// Add a simple string context to the error
    fn context(self, msg: &str) -> Result<T>;

    /// Add context using a closure (for expensive formatting)
    fn with_context<F>(self, f: F) -> Result<T>
    where
        F: FnOnce() -> String;
}

impl<T> ErrorContext<T> for Result<T> {
    fn context(self, msg: &str) -> Result<T> {
        self.map_err(|e| StrandError::Internal(format!("{msg}: {e}")))
    }

    fn with_context<F>(self, f: F) -> Result<T>
    where
        F: FnOnce() -> String,
    {
        self.map_err(|e| StrandError::Internal(format!("{}: {}", f(), e)))
    }
}

/// Error codes for external APIs
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ErrorCode {
    // Storage errors (1000-1999)
    StorageIo = 1001,
    BlockNotFound = 1002,
    CorruptedBlock = 1003,

    // Crypto errors (2000-2999)
    InvalidSignature = 2001,
    InvalidKey = 2002,

    // Protocol errors (3000-3999)
    VersionMismatch = 3001,
    Timeout = 3002,

    // Ownership errors (4000-4999)
    PermissionDenied = 4001,
    InvalidDid = 4002,

    // Replication errors (5000-5999)
    SyncFailed = 5001,
    ForkDetected = 5002,

    // Validation errors (6000-6999)
    ValidationFailed = 6001,
    ChecksumMismatch = 6002,

    // General errors
    InternalError = 9001,
    NotImplemented = 9002,
}

impl From<&StrandError> for ErrorCode {
    fn from(err: &StrandError) -> Self {
        match err {
            StrandError::Storage(StorageError::BlockNotFound(_)) => ErrorCode::BlockNotFound,
            StrandError::Storage(StorageError::CorruptedBlock { .. }) => ErrorCode::CorruptedBlock,
            StrandError::Storage(_) => ErrorCode::StorageIo,

            StrandError::Crypto(CryptoError::InvalidSignature) => ErrorCode::InvalidSignature,
            StrandError::Crypto(_) => ErrorCode::InvalidKey,

            StrandError::Protocol(ProtocolError::VersionMismatch { .. }) => {
                ErrorCode::VersionMismatch
            }
            StrandError::Protocol(ProtocolError::Timeout(_)) => ErrorCode::Timeout,
            StrandError::Protocol(_) => ErrorCode::Timeout,

            StrandError::Ownership(OwnershipError::PermissionDenied { .. }) => {
                ErrorCode::PermissionDenied
            }
            StrandError::Ownership(OwnershipError::InvalidAgentIdentity(_)) => {
                ErrorCode::InvalidDid
            }
            StrandError::Ownership(OwnershipError::InvalidOrganizationIdentity(_)) => {
                ErrorCode::InvalidDid
            }
            StrandError::Ownership(_) => ErrorCode::PermissionDenied,

            StrandError::Replication(ReplicationError::SyncFailed(_)) => ErrorCode::SyncFailed,
            StrandError::Replication(ReplicationError::ForkDetected(_)) => ErrorCode::ForkDetected,
            StrandError::Replication(_) => ErrorCode::SyncFailed,

            StrandError::Validation(ValidationError::ChecksumMismatch) => {
                ErrorCode::ChecksumMismatch
            }
            StrandError::Validation(_) => ErrorCode::ValidationFailed,

            StrandError::Internal(_) => ErrorCode::InternalError,
            StrandError::NotImplemented(_) => ErrorCode::NotImplemented,
        }
    }
}

/// Extension to map errors to io::ErrorKind when appropriate
pub trait ToIoErrorKind {
    /// Returns a corresponding io::ErrorKind when a meaningful mapping exists
    fn to_io_error_kind(&self) -> Option<io::ErrorKind>;
}

impl ToIoErrorKind for StrandError {
    fn to_io_error_kind(&self) -> Option<io::ErrorKind> {
        match self {
            // Storage
            StrandError::Storage(StorageError::Io(e)) => Some(e.kind()),
            StrandError::Storage(StorageError::BlockNotFound(_)) => Some(io::ErrorKind::NotFound),
            StrandError::Storage(StorageError::CorruptedBlock { .. }) => {
                Some(io::ErrorKind::InvalidData)
            }
            StrandError::Storage(StorageError::BackendUnavailable(_)) => {
                Some(io::ErrorKind::NotConnected)
            }
            StrandError::Storage(StorageError::InsufficientSpace { .. }) => {
                Some(io::ErrorKind::Other)
            }
            StrandError::Storage(StorageError::InvalidPath(_)) => Some(io::ErrorKind::InvalidInput),

            // Crypto
            StrandError::Crypto(CryptoError::InvalidSignature) => Some(io::ErrorKind::InvalidData),
            StrandError::Crypto(CryptoError::InvalidPublicKey(_)) => {
                Some(io::ErrorKind::InvalidInput)
            }
            StrandError::Crypto(CryptoError::InvalidSecretKey) => Some(io::ErrorKind::InvalidInput),
            StrandError::Crypto(CryptoError::KeyDerivationFailed(_)) => Some(io::ErrorKind::Other),
            StrandError::Crypto(CryptoError::EncryptionFailed(_)) => Some(io::ErrorKind::Other),
            StrandError::Crypto(CryptoError::DecryptionFailed(_)) => Some(io::ErrorKind::Other),
            StrandError::Crypto(CryptoError::InvalidKeyLength { .. }) => {
                Some(io::ErrorKind::InvalidInput)
            }

            // Protocol
            StrandError::Protocol(ProtocolError::VersionMismatch { .. }) => {
                Some(io::ErrorKind::Unsupported)
            }
            StrandError::Protocol(ProtocolError::InvalidMessageType(_)) => {
                Some(io::ErrorKind::InvalidData)
            }
            StrandError::Protocol(ProtocolError::Timeout(_)) => Some(io::ErrorKind::TimedOut),
            StrandError::Protocol(ProtocolError::InvalidStateTransition { .. }) => {
                Some(io::ErrorKind::InvalidData)
            }
            StrandError::Protocol(ProtocolError::HandshakeFailed(_)) => {
                Some(io::ErrorKind::ConnectionAborted)
            }
            StrandError::Protocol(ProtocolError::ChannelClosed) => Some(io::ErrorKind::BrokenPipe),
            StrandError::Protocol(ProtocolError::MessageTooLarge { .. }) => {
                Some(io::ErrorKind::InvalidData)
            }

            // Ownership
            StrandError::Ownership(OwnershipError::InvalidAgentIdentity(_)) => {
                Some(io::ErrorKind::InvalidInput)
            }
            StrandError::Ownership(OwnershipError::InvalidOrganizationIdentity(_)) => {
                Some(io::ErrorKind::InvalidInput)
            }
            StrandError::Ownership(OwnershipError::AgentNotAuthorized { .. }) => {
                Some(io::ErrorKind::PermissionDenied)
            }
            StrandError::Ownership(OwnershipError::AgentNotInOrganization { .. }) => {
                Some(io::ErrorKind::PermissionDenied)
            }
            StrandError::Ownership(OwnershipError::WriterMismatch { .. }) => {
                Some(io::ErrorKind::PermissionDenied)
            }
            StrandError::Ownership(OwnershipError::OrganizationVerificationFailed(_)) => {
                Some(io::ErrorKind::Other)
            }
            StrandError::Ownership(OwnershipError::MultiSignatoryConsensusFailed { .. }) => {
                Some(io::ErrorKind::PermissionDenied)
            }
            StrandError::Ownership(OwnershipError::GovernanceApprovalRequired { .. }) => {
                Some(io::ErrorKind::PermissionDenied)
            }
            StrandError::Ownership(OwnershipError::PermissionDenied { .. }) => {
                Some(io::ErrorKind::PermissionDenied)
            }
            StrandError::Ownership(OwnershipError::IdentityResolutionFailed(_)) => {
                Some(io::ErrorKind::NotFound)
            }
            StrandError::Ownership(OwnershipError::InvalidSignatureForIdentity { .. }) => {
                Some(io::ErrorKind::InvalidData)
            }

            // Replication
            StrandError::Replication(ReplicationError::SyncFailed(_)) => Some(io::ErrorKind::Other),
            StrandError::Replication(ReplicationError::ForkDetected(_)) => {
                Some(io::ErrorKind::Other)
            }
            StrandError::Replication(ReplicationError::InvalidProof(_)) => {
                Some(io::ErrorKind::InvalidData)
            }
            StrandError::Replication(ReplicationError::PeerDisconnected) => {
                Some(io::ErrorKind::ConnectionReset)
            }
            StrandError::Replication(ReplicationError::BandwidthExceeded { .. }) => {
                Some(io::ErrorKind::Other)
            }

            // Validation
            StrandError::Validation(ValidationError::InvalidBlockSize(_)) => {
                Some(io::ErrorKind::InvalidData)
            }
            StrandError::Validation(ValidationError::InvalidTreeState(_)) => {
                Some(io::ErrorKind::InvalidData)
            }
            StrandError::Validation(ValidationError::ChecksumMismatch) => {
                Some(io::ErrorKind::InvalidData)
            }
            StrandError::Validation(ValidationError::InvalidIndex(_)) => {
                Some(io::ErrorKind::InvalidInput)
            }
            StrandError::Validation(ValidationError::SchemaValidation(_)) => {
                Some(io::ErrorKind::InvalidData)
            }

            // General
            StrandError::Internal(_) => Some(io::ErrorKind::Other),
            StrandError::NotImplemented(_) => Some(io::ErrorKind::Unsupported),
        }
    }
}

impl From<StrandError> for io::Error {
    fn from(err: StrandError) -> Self {
        let kind = err.to_io_error_kind().unwrap_or(io::ErrorKind::Other);
        io::Error::new(kind, err.to_string())
    }
}

#[cfg(feature = "serde")]
#[derive(Debug, Serialize, Deserialize)]
pub struct SerializedError {
    pub code: ErrorCode,
    pub message: String,
    pub details: Option<serde_json::Value>,
}

#[cfg(feature = "serde")]
impl From<StrandError> for SerializedError {
    fn from(err: StrandError) -> Self {
        SerializedError {
            code: ErrorCode::from(&err),
            message: err.to_string(),
            details: None,
        }
    }
}

/// Compatibility with JavaScript error names
impl StrandError {
    /// Get JavaScript-compatible error name
    pub fn error_name(&self) -> &'static str {
        match self {
            // Storage Errors
            StrandError::Storage(StorageError::Io(_)) => "STORAGE_IO_ERROR",
            StrandError::Storage(StorageError::BlockNotFound(_)) => "BLOCK_NOT_FOUND",
            StrandError::Storage(StorageError::CorruptedBlock { .. }) => "CORRUPTED_BLOCK",
            StrandError::Storage(StorageError::BackendUnavailable(_)) => "BACKEND_UNAVAILABLE",
            StrandError::Storage(StorageError::InsufficientSpace { .. }) => "INSUFFICIENT_SPACE",
            StrandError::Storage(StorageError::InvalidPath(_)) => "INVALID_STORAGE_PATH",

            // Crypto Errors
            StrandError::Crypto(CryptoError::InvalidSignature) => "INVALID_SIGNATURE",
            StrandError::Crypto(CryptoError::InvalidPublicKey(_)) => "INVALID_PUBLIC_KEY",
            StrandError::Crypto(CryptoError::InvalidSecretKey) => "INVALID_SECRET_KEY",
            StrandError::Crypto(CryptoError::KeyDerivationFailed(_)) => "KEY_DERIVATION_FAILED",
            StrandError::Crypto(CryptoError::EncryptionFailed(_)) => "ENCRYPTION_FAILED",
            StrandError::Crypto(CryptoError::DecryptionFailed(_)) => "DECRYPTION_FAILED",
            StrandError::Crypto(CryptoError::InvalidKeyLength { .. }) => "INVALID_KEY_LENGTH",

            // Protocol Errors
            StrandError::Protocol(ProtocolError::VersionMismatch { .. }) => {
                "PROTOCOL_VERSION_MISMATCH"
            }
            StrandError::Protocol(ProtocolError::InvalidMessageType(_)) => "INVALID_MESSAGE_TYPE",
            StrandError::Protocol(ProtocolError::Timeout(_)) => "PROTOCOL_TIMEOUT",
            StrandError::Protocol(ProtocolError::InvalidStateTransition { .. }) => {
                "INVALID_STATE_TRANSITION"
            }
            StrandError::Protocol(ProtocolError::HandshakeFailed(_)) => "HANDSHAKE_FAILED",
            StrandError::Protocol(ProtocolError::ChannelClosed) => "CHANNEL_CLOSED",
            StrandError::Protocol(ProtocolError::MessageTooLarge { .. }) => "MESSAGE_TOO_LARGE",

            // Ownership Errors
            StrandError::Ownership(OwnershipError::InvalidAgentIdentity(_)) => {
                "INVALID_AGENT_IDENTITY"
            }
            StrandError::Ownership(OwnershipError::InvalidOrganizationIdentity(_)) => {
                "INVALID_ORGANIZATION_IDENTITY"
            }
            StrandError::Ownership(OwnershipError::AgentNotAuthorized { .. }) => {
                "AGENT_NOT_AUTHORIZED"
            }
            StrandError::Ownership(OwnershipError::AgentNotInOrganization { .. }) => {
                "AGENT_NOT_IN_ORGANIZATION"
            }
            StrandError::Ownership(OwnershipError::WriterMismatch { .. }) => "WRITER_MISMATCH",
            StrandError::Ownership(OwnershipError::OrganizationVerificationFailed(_)) => {
                "ORGANIZATION_VERIFICATION_FAILED"
            }
            StrandError::Ownership(OwnershipError::MultiSignatoryConsensusFailed { .. }) => {
                "MULTI_SIGNATORY_CONSENSUS_FAILED"
            }
            StrandError::Ownership(OwnershipError::GovernanceApprovalRequired { .. }) => {
                "GOVERNANCE_APPROVAL_REQUIRED"
            }
            StrandError::Ownership(OwnershipError::PermissionDenied { .. }) => "PERMISSION_DENIED",
            StrandError::Ownership(OwnershipError::IdentityResolutionFailed(_)) => {
                "IDENTITY_RESOLUTION_FAILED"
            }
            StrandError::Ownership(OwnershipError::InvalidSignatureForIdentity { .. }) => {
                "INVALID_SIGNATURE_FOR_IDENTITY"
            }

            // Replication Errors
            StrandError::Replication(ReplicationError::SyncFailed(_)) => "SYNC_FAILED",
            StrandError::Replication(ReplicationError::ForkDetected(_)) => "FORK_DETECTED",
            StrandError::Replication(ReplicationError::InvalidProof(_)) => "INVALID_PROOF",
            StrandError::Replication(ReplicationError::PeerDisconnected) => "PEER_DISCONNECTED",
            StrandError::Replication(ReplicationError::BandwidthExceeded { .. }) => {
                "BANDWIDTH_EXCEEDED"
            }

            // Validation Errors
            StrandError::Validation(ValidationError::InvalidBlockSize(_)) => "INVALID_BLOCK_SIZE",
            StrandError::Validation(ValidationError::InvalidTreeState(_)) => "INVALID_TREE_STATE",
            StrandError::Validation(ValidationError::ChecksumMismatch) => "CHECKSUM_MISMATCH",
            StrandError::Validation(ValidationError::InvalidIndex(_)) => "INVALID_INDEX",
            StrandError::Validation(ValidationError::SchemaValidation(_)) => {
                "SCHEMA_VALIDATION_FAILED"
            }

            // General Errors
            StrandError::Internal(_) => "INTERNAL_ERROR",
            StrandError::NotImplemented(_) => "NOT_IMPLEMENTED",
        }
    }
}

/// Test utilities for error handling
#[cfg(test)]
pub mod test_utils {
    use super::*;

    /// Assert that an error matches a specific variant
    pub fn assert_error_kind<T>(result: Result<T>, expected: &str) {
        match result {
            Err(e) => assert_eq!(e.error_name(), expected),
            Ok(_) => panic!("Expected error, got Ok"),
        }
    }

    /// Create test errors for unit tests
    pub fn test_storage_error() -> StrandError {
        StorageError::BlockNotFound(42).into()
    }

    pub fn test_crypto_error() -> StrandError {
        CryptoError::InvalidSignature.into()
    }

    pub fn test_ownership_error() -> StrandError {
        OwnershipError::PermissionDenied {
            identity: "did:l1fe:agent:test".to_string(),
            permission: "write".to_string(),
        }
        .into()
    }
}

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

    #[test]
    fn test_error_conversion() {
        let storage_err = StorageError::BlockNotFound(42);
        let strand_err: StrandError = storage_err.into();
        assert_eq!(strand_err.error_name(), "BLOCK_NOT_FOUND");

        let crypto_err = CryptoError::InvalidSignature;
        let strand_err: StrandError = crypto_err.into();
        assert_eq!(strand_err.error_name(), "INVALID_SIGNATURE");
    }

    #[test]
    fn test_error_code_conversion() {
        let err = StrandError::Storage(StorageError::BlockNotFound(42));
        let code = ErrorCode::from(&err);
        assert_eq!(code, ErrorCode::BlockNotFound);
    }

    #[test]
    fn test_error_context() {
        fn fallible_operation() -> Result<()> {
            Err(StorageError::BlockNotFound(42).into())
        }

        let result = fallible_operation().context("Failed to perform operation");
        assert!(result.is_err());
        let err_msg = result.unwrap_err().to_string();
        assert!(err_msg.contains("Failed to perform operation"));
    }

    #[test]
    fn test_ownership_errors() {
        let err = OwnershipError::AgentNotAuthorized {
            agent_identity: "did:l1fe:agent:test".to_string(),
            action: "write".to_string(),
        };
        let strand_err: StrandError = err.into();
        match strand_err {
            StrandError::Ownership(OwnershipError::AgentNotAuthorized {
                agent_identity,
                action,
            }) => {
                assert_eq!(agent_identity, "did:l1fe:agent:test");
                assert_eq!(action, "write");
            }
            _ => panic!("Wrong error type"),
        }
    }

    #[test]
    fn test_io_error_conversion() {
        use std::io;
        let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
        let storage_err = StorageError::from(io_err);
        let strand_err: StrandError = storage_err.into();
        assert!(matches!(
            strand_err,
            StrandError::Storage(StorageError::Io(_))
        ));
    }

    #[cfg(feature = "serde")]
    #[test]
    fn test_error_serialization() {
        let err = StrandError::Storage(StorageError::BlockNotFound(42));
        let serialized = SerializedError::from(err);
        assert_eq!(serialized.code, ErrorCode::BlockNotFound);
        assert!(serialized.message.contains("Block not found: index 42"));
    }

    #[test]
    fn test_io_kind_mapping_storage_not_found() {
        let err: StrandError = StorageError::BlockNotFound(7).into();
        assert_eq!(err.to_io_error_kind(), Some(io::ErrorKind::NotFound));
    }

    #[test]
    fn test_io_kind_mapping_protocol_timeout() {
        let err: StrandError = ProtocolError::Timeout(30).into();
        assert_eq!(err.to_io_error_kind(), Some(io::ErrorKind::TimedOut));
    }

    #[test]
    fn test_io_kind_mapping_permission_denied() {
        let err: StrandError = OwnershipError::PermissionDenied {
            identity: "did:l1fe:agent:test".to_string(),
            permission: "write".to_string(),
        }
        .into();
        assert_eq!(
            err.to_io_error_kind(),
            Some(io::ErrorKind::PermissionDenied)
        );
    }

    #[test]
    fn test_io_error_from_strand_error_preserves_kind() {
        let err: StrandError = ProtocolError::ChannelClosed.into();
        let io_err: io::Error = err.into();
        assert_eq!(io_err.kind(), io::ErrorKind::BrokenPipe);
    }

    #[test]
    fn test_display_stability_examples() {
        let e1: StrandError = StorageError::BlockNotFound(42).into();
        assert_eq!(e1.to_string(), "Block not found: index 42");

        let e2: StrandError = ProtocolError::VersionMismatch {
            local: 1,
            remote: 2,
        }
        .into();
        assert_eq!(e2.to_string(), "Version mismatch: local 1, remote 2");
    }

    #[test]
    fn test_downcast_to_io_error_source() {
        let source = io::Error::new(io::ErrorKind::NotFound, "missing");
        let storage_err = StorageError::from(source);
        let strand_err: StrandError = storage_err.into();

        // Ensure variant matches
        if let StrandError::Storage(StorageError::Io(inner)) = &strand_err {
            assert_eq!(inner.kind(), io::ErrorKind::NotFound);
        } else {
            panic!("expected Storage::Io variant");
        }

        // Ensure std::error::Error source chain is present and type is io::Error
        let source = std::error::Error::source(&strand_err).expect("expected source error");
        let _io_ref = source
            .downcast_ref::<io::Error>()
            .expect("source should be io::Error");
    }
}
