// Copyright 2024 Saorsa Labs Limited
//
// This software is dual-licensed under:
// - GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later)
// - Commercial License
//
// For AGPL-3.0 license, see LICENSE-AGPL-3.0
// For commercial licensing, contact: david@saorsalabs.com
//
// Unless required by applicable law or agreed to in writing, software
// distributed under these licenses is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

//! Threshold cryptography module
//!
//! Implements FROST (Flexible Round-Optimized Schnorr Threshold) signatures
//! and dynamic group management with Byzantine fault tolerance.

pub mod dkg;
pub mod frost;
pub mod group;

pub use self::group::*;

use crate::quantum_crypto::types::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::SystemTime;
use thiserror::Error;

/// Threshold cryptography errors
#[derive(Debug, Error)]
pub enum ThresholdError {
    #[error("Invalid threshold parameters: {0}")]
    InvalidParameters(String),

    #[error("Insufficient participants: need {required}, have {available}")]
    InsufficientParticipants { required: u16, available: u16 },

    #[error("Invalid share: {0}")]
    InvalidShare(String),

    #[error("DKG ceremony failed: {0}")]
    DkgFailed(String),

    #[error("Signature aggregation failed: {0}")]
    AggregationFailed(String),

    #[error("Group operation failed: {0}")]
    GroupOperationFailed(String),

    #[error("Consensus failed: {0}")]
    ConsensusFailed(String),

    #[error("Participant not found: {0}")]
    ParticipantNotFound(ParticipantId),

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

/// Result type for threshold operations
pub type Result<T> = std::result::Result<T, ThresholdError>;

/// Threshold signature type (placeholder)
pub type ThresholdSignature = Vec<u8>;

/// Threshold group with dynamic membership
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThresholdGroup {
    /// Unique group identifier
    pub group_id: GroupId,

    /// Current threshold (t in t-of-n)
    pub threshold: u16,

    /// Total participants (n in t-of-n)
    pub participants: u16,

    /// FROST group public key
    pub frost_group_key: FrostGroupPublicKey,

    /// Active participants with their shares
    pub active_participants: Vec<ParticipantInfo>,

    /// Participants being added
    pub pending_participants: Vec<ParticipantInfo>,

    /// Group version (incremented on changes)
    pub version: u64,

    /// Group metadata
    pub metadata: GroupMetadata,

    /// Audit log of group operations
    pub audit_log: Vec<GroupAuditEntry>,

    /// Creation timestamp
    pub created_at: SystemTime,

    /// Last update timestamp
    pub last_updated: SystemTime,
}

/// Participant information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParticipantInfo {
    /// Unique participant identifier
    pub participant_id: ParticipantId,

    /// ML-DSA public key for authentication (serialized ant-quic type)
    pub public_key: Vec<u8>, // Serialized ant_quic::crypto::pqc::types::MlDsaPublicKey

    /// FROST share commitment
    pub frost_share_commitment: FrostCommitment,

    /// Participant role in the group
    pub role: ParticipantRole,

    /// Status in the group
    pub status: ParticipantStatus,

    /// Join timestamp
    pub joined_at: SystemTime,

    /// Custom metadata
    pub metadata: HashMap<String, String>,
}

/// Participant roles with hierarchical permissions
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ParticipantRole {
    /// Can initiate all group operations
    Leader { permissions: LeaderPermissions },

    /// Can participate in threshold operations
    Member { permissions: MemberPermissions },

    /// Read-only access
    Observer,
}

/// Leader permissions
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LeaderPermissions {
    pub can_add_participants: bool,
    pub can_remove_participants: bool,
    pub can_update_threshold: bool,
    pub can_initiate_refresh: bool,
    pub can_assign_roles: bool,
    pub can_create_subgroups: bool,
}

impl Default for LeaderPermissions {
    fn default() -> Self {
        Self {
            can_add_participants: true,
            can_remove_participants: true,
            can_update_threshold: true,
            can_initiate_refresh: true,
            can_assign_roles: true,
            can_create_subgroups: true,
        }
    }
}

/// Member permissions
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MemberPermissions {
    pub can_sign: bool,
    pub can_propose_operations: bool,
    pub can_vote: bool,
}

impl Default for MemberPermissions {
    fn default() -> Self {
        Self {
            can_sign: true,
            can_propose_operations: true,
            can_vote: true,
        }
    }
}

/// Participant status
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ParticipantStatus {
    /// Active and can participate
    Active,

    /// Waiting for key ceremony completion
    PendingJoin,

    /// Marked for removal in next refresh
    PendingRemoval,

    /// Temporarily offline but still valid
    Inactive,

    /// Suspended due to misbehavior
    Suspended { reason: String, until: SystemTime },
}

/// Group metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GroupMetadata {
    pub name: String,
    pub description: String,
    pub purpose: GroupPurpose,
    pub parent_group: Option<GroupId>,
    pub custom_data: HashMap<String, String>,
}

/// Group purpose
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum GroupPurpose {
    /// General multi-signature
    MultiSig,

    /// Key management
    KeyManagement,

    /// Access control
    AccessControl,

    /// Governance decisions
    Governance,

    /// Custom purpose
    Custom(String),
}

/// Group audit entry
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GroupAuditEntry {
    pub timestamp: SystemTime,
    pub operation: GroupOperation,
    pub initiator: ParticipantId,
    pub approvers: Vec<ParticipantId>,
    pub result: OperationResult,
    pub metadata: HashMap<String, String>,
}

/// Group operation types
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GroupOperation {
    /// Add new participant
    AddParticipant {
        group_id: GroupId,
        new_participant: ParticipantInfo,
        new_threshold: Option<u16>,
    },

    /// Remove existing participant
    RemoveParticipant {
        group_id: GroupId,
        participant_id: ParticipantId,
        new_threshold: Option<u16>,
    },

    /// Update threshold value
    UpdateThreshold {
        group_id: GroupId,
        new_threshold: u16,
    },

    /// Refresh keys (proactive security)
    RefreshKeys { group_id: GroupId },

    /// Update participant role
    UpdateRole {
        group_id: GroupId,
        participant_id: ParticipantId,
        new_role: ParticipantRole,
    },

    /// Suspend participant
    SuspendParticipant {
        group_id: GroupId,
        participant_id: ParticipantId,
        reason: String,
        duration: std::time::Duration,
    },

    /// Create subgroup
    CreateSubgroup {
        parent_group_id: GroupId,
        subgroup_config: SubgroupConfig,
    },
}

/// Operation result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OperationResult {
    Success,
    Failed(String),
    Pending,
}

/// Subgroup configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubgroupConfig {
    pub name: String,
    pub threshold: u16,
    pub initial_participants: Vec<ParticipantId>,
    pub purpose: GroupPurpose,
}

/// Threshold group manager
pub struct ThresholdGroupManager {
    /// All managed groups
    pub groups: HashMap<GroupId, ThresholdGroup>,

    /// Local participant's shares for each group
    pub local_shares: HashMap<GroupId, FrostKeyShare>,

    /// Pending operations awaiting consensus
    pub pending_operations: HashMap<OperationId, PendingOperation>,

    /// Local participant identity
    pub local_identity: QuantumPeerIdentity,
}

/// Operation identifier
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct OperationId([u8; 32]);

/// Pending operation awaiting consensus
#[derive(Debug, Clone)]
pub struct PendingOperation {
    pub id: OperationId,
    pub operation: GroupOperation,
    pub proposed_at: SystemTime,
    pub proposer: ParticipantId,
    pub approvals: Vec<ParticipantApproval>,
    pub rejections: Vec<ParticipantRejection>,
    pub status: ConsensusStatus,
}

/// Participant approval
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParticipantApproval {
    pub participant_id: ParticipantId,
    pub signature: Vec<u8>, // Serialized ant_quic::crypto::pqc::types::MlDsaSignature
    pub timestamp: SystemTime,
}

/// Participant rejection
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParticipantRejection {
    pub participant_id: ParticipantId,
    pub reason: String,
    pub signature: Vec<u8>, // Serialized ant_quic::crypto::pqc::types::MlDsaSignature
    pub timestamp: SystemTime,
}

/// Consensus status
#[derive(Debug, Clone, PartialEq)]
pub enum ConsensusStatus {
    /// Waiting for more approvals
    Pending,

    /// Approved by threshold
    Approved,

    /// Rejected by threshold
    Rejected,

    /// Timed out
    TimedOut,
}

impl ThresholdGroupManager {
    /// Create a new threshold group manager
    pub fn new(local_identity: QuantumPeerIdentity) -> Self {
        Self {
            groups: HashMap::new(),
            local_shares: HashMap::new(),
            pending_operations: HashMap::new(),
            local_identity,
        }
    }

    /// Create a new threshold group
    pub async fn create_group(&mut self, config: GroupConfig) -> Result<ThresholdGroup> {
        // Validate parameters
        if config.threshold > config.participants.len() as u16 {
            return Err(ThresholdError::InvalidParameters(
                "Threshold cannot exceed number of participants".to_string(),
            ));
        }

        if config.threshold == 0 {
            return Err(ThresholdError::InvalidParameters(
                "Threshold must be at least 1".to_string(),
            ));
        }

        // Initiate DKG ceremony
        let dkg_result = dkg::run_ceremony(config.threshold, config.participants.clone()).await?;

        // Create group
        let group = ThresholdGroup {
            group_id: GroupId(rand::random()),
            threshold: config.threshold,
            participants: config.participants.len() as u16,
            frost_group_key: dkg_result.group_key,
            active_participants: config.participants.clone(),
            pending_participants: Vec::new(),
            version: 1,
            metadata: config.metadata.clone(),
            audit_log: vec![GroupAuditEntry {
                timestamp: SystemTime::now(),
                operation: GroupOperation::CreateSubgroup {
                    parent_group_id: GroupId([0; 32]),
                    subgroup_config: SubgroupConfig {
                        name: config.metadata.name.clone(),
                        threshold: config.threshold,
                        initial_participants: config
                            .participants
                            .iter()
                            .map(|p| p.participant_id.clone())
                            .collect(),
                        purpose: config.metadata.purpose.clone(),
                    },
                },
                initiator: self.local_identity.peer_id.clone().into(),
                approvers: vec![],
                result: OperationResult::Success,
                metadata: HashMap::new(),
            }],
            created_at: SystemTime::now(),
            last_updated: SystemTime::now(),
        };

        // Store group and local share
        self.groups.insert(group.group_id.clone(), group.clone());
        self.local_shares
            .insert(group.group_id.clone(), dkg_result.local_share);

        Ok(group)
    }

    /// Propose a group operation
    pub async fn propose_operation(&mut self, operation: GroupOperation) -> Result<OperationId> {
        let operation_id = OperationId(rand::random());

        let pending_op = PendingOperation {
            id: operation_id.clone(),
            operation,
            proposed_at: SystemTime::now(),
            proposer: self.local_identity.peer_id.clone().into(),
            approvals: vec![],
            rejections: vec![],
            status: ConsensusStatus::Pending,
        };

        self.pending_operations
            .insert(operation_id.clone(), pending_op);

        // Broadcast proposal to group members
        // Implementation would send network messages here

        Ok(operation_id)
    }
}

/// Group configuration for creation
pub struct GroupConfig {
    pub threshold: u16,
    pub participants: Vec<ParticipantInfo>,
    pub metadata: GroupMetadata,
}

/// Convert PeerId to ParticipantId (simplified for example)
impl From<PeerId> for ParticipantId {
    fn from(_peer_id: PeerId) -> Self {
        // In practice, this would maintain a proper mapping
        ParticipantId(0)
    }
}

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

    #[test]
    fn test_permission_defaults() {
        let leader_perms = LeaderPermissions::default();
        assert!(leader_perms.can_add_participants);
        assert!(leader_perms.can_remove_participants);

        let member_perms = MemberPermissions::default();
        assert!(member_perms.can_sign);
        assert!(member_perms.can_vote);
    }

    #[test]
    fn test_participant_roles() {
        let leader = ParticipantRole::Leader {
            permissions: LeaderPermissions::default(),
        };

        let member = ParticipantRole::Member {
            permissions: MemberPermissions::default(),
        };

        assert_ne!(leader, member);
    }
}
