use std::sync::Arc;

use anyhow::Result;
use async_trait::async_trait;
use tycho_crypto::ed25519::PublicKey;
use tycho_network::{Network, OverlayService, PeerId, PeerResolver};
use tycho_types::cell::HashBytes;
use tycho_types::models::{BlockId, BlockIdShort, IndexedValidatorDescription, ShardIdent};
use tycho_util::FastHashMap;

pub use self::impls::*;

pub mod proto;
pub mod rpc;

mod impls {
    pub use self::std_impl::{ValidatorStdImpl, ValidatorStdImplConfig};

    mod std_impl;
}

// === Validator ===

#[async_trait]
pub trait Validator: Send + Sync + 'static {
    /// Adds a new session for the specified shard.
    fn add_session(&self, info: AddSession<'_>) -> Result<()>;

    /// Collects signatures for the specified block.
    async fn validate(
        &self,
        session_id: ValidationSessionId,
        block_id: &BlockId,
    ) -> Result<ValidationStatus>;

    /// Cancels validation before the specified block.
    ///
    /// If `session_id` is provided, it will be used to directly cancel the specific session,
    /// avoiding unnecessary lookups and potential ambiguity.
    fn cancel_validation(
        &self,
        before: &BlockIdShort,
        session_id: Option<ValidationSessionId>,
    ) -> Result<()>;
}

// === Types ===

pub struct ValidatorNetworkContext {
    pub network: Network,
    pub peer_resolver: PeerResolver,
    pub overlays: OverlayService,
    pub zerostate_id: BlockId,
}

/// (`catchain_seqno`, `vset_switch_round`)
pub type ValidationSessionId = (u32, u32);

pub trait CompositeValidationSessionId {
    fn catchain_seqno(&self) -> u32;
    fn vset_switch_round(&self) -> u32;
}

impl CompositeValidationSessionId for ValidationSessionId {
    fn catchain_seqno(&self) -> u32 {
        self.0
    }

    fn vset_switch_round(&self) -> u32 {
        self.1
    }
}

#[derive(Debug, Clone, Copy)]
pub struct AddSession<'a> {
    pub shard_ident: ShardIdent,
    pub start_block_seqno: u32,
    pub session_id: ValidationSessionId,
    pub vset_hash: &'a HashBytes,
    pub validators: &'a [IndexedValidatorDescription],
}

#[derive(Debug, Clone)]
pub enum ValidationStatus {
    Skipped,
    Complete(ValidationComplete),
}

#[derive(Debug, Clone)]
pub struct ValidationComplete {
    pub signatures: BlockSignatures,
    pub total_weight: u64,
}

pub type BlockSignatures = FastHashMap<PeerId, Arc<[u8; 64]>>;

#[derive(Debug, Clone)]
pub struct BriefValidatorDescr {
    pub peer_id: PeerId,
    pub public_key: PublicKey,
    pub weight: u64,
    pub validator_idx: u16,
}

impl BriefValidatorDescr {
    pub fn from_descr(descr: &IndexedValidatorDescription) -> Result<Self> {
        let Some(public_key) = PublicKey::from_bytes(descr.public_key.0) else {
            anyhow::bail!("invalid validator public key");
        };

        Ok(Self {
            peer_id: PeerId(descr.public_key.0),
            public_key,
            weight: descr.weight,
            validator_idx: descr.validator_idx,
        })
    }
}
