//! Protobuf types and `ConnectRPC` stubs generated from `proto/makechain.proto`.

// Pre-existing long codec function; suppressed at crate level.
#![allow(clippy::too_many_lines)]

// connectrpc-build emitted `_connectrpc.rs`. It re-exports the buffa-generated
// message types for the `makechain` package, the ConnectRPC service trait
// (`MakechainService` server side), and the typed client.
//
// The pull-in macro flattens the per-package nesting into the crate root so
// downstream consumers can keep their existing `use makechain_proto::Message`
// style imports unchanged.
mod generated {
    // unreachable_pub: generated code declares pub items not reachable from the crate root.
    #![allow(
        clippy::all,
        clippy::pedantic,
        missing_debug_implementations,
        unreachable_pub
    )]
    connectrpc::include_generated!();
}

// Re-export every type out of the generated `makechain` package as if it lived
// at the crate root, preserving the public surface from the pre-buffa era so
// downstream crates don't have to update import paths.
pub use generated::makechain::*;

// `commonware-codec` `Write + Read + EncodeSize` impls for the consensus-critical
// protobuf types: `MessageData`, every body variant in the oneof, and the nested
// types they reach. Pinned by `MessageDataEncoding` in
// `crates/makechain-consensus/tests/conformance_test.rs`.
mod codec;

// `commonware-codec` impls for the consensus envelope types reachable from
// `BlockHeader` — `Height`, `ConsensusContext`, `BlockHeader`. The canonical
// block-identity input is
// `keccak256(commonware_codec::encode(&block_header).to_vec())`, so these
// impls form the canonical hash chain alongside `MessageData`. Pinned by
// `BlockHeaderEncoding` in
// `crates/makechain-consensus/tests/conformance_test.rs`.
mod block_codec;

impl MessageType {
    /// Return a static string label for this message type.
    ///
    /// Used for Prometheus metric labels and display to avoid
    /// repeated `format!("{:?}",...)` allocations.
    #[must_use]
    pub fn as_str(self) -> &'static str {
        match <Self as ::buffa::Enumeration>::proto_name(&self) {
            "MESSAGE_TYPE_NONE" => "None",
            "MESSAGE_TYPE_PROJECT_CREATE" => "ProjectCreate",
            "MESSAGE_TYPE_PROJECT_REMOVE" => "ProjectRemove",
            "MESSAGE_TYPE_PROJECT_METADATA" => "ProjectMetadata",
            "MESSAGE_TYPE_PROJECT_ARCHIVE" => "ProjectArchive",
            "MESSAGE_TYPE_FORK" => "Fork",
            "MESSAGE_TYPE_REF_UPDATE" => "RefUpdate",
            "MESSAGE_TYPE_REF_DELETE" => "RefDelete",
            "MESSAGE_TYPE_COMMIT_BUNDLE" => "CommitBundle",
            "MESSAGE_TYPE_COLLABORATOR_ADD" => "CollaboratorAdd",
            "MESSAGE_TYPE_COLLABORATOR_REMOVE" => "CollaboratorRemove",
            "MESSAGE_TYPE_ACCOUNT_DATA" => "AccountData",
            "MESSAGE_TYPE_VERIFICATION_ADD" => "VerificationAdd",
            "MESSAGE_TYPE_VERIFICATION_REMOVE" => "VerificationRemove",
            "MESSAGE_TYPE_STORAGE_CLAIM" => "StorageClaim",
            "MESSAGE_TYPE_USERNAME_CREATE" => "UsernameCreate",
            "MESSAGE_TYPE_USERNAME_UPDATE" => "UsernameUpdate",
            "MESSAGE_TYPE_LINK_ADD" => "LinkAdd",
            "MESSAGE_TYPE_LINK_REMOVE" => "LinkRemove",
            "MESSAGE_TYPE_REACTION_ADD" => "ReactionAdd",
            "MESSAGE_TYPE_REACTION_REMOVE" => "ReactionRemove",
            "MESSAGE_TYPE_SIGNER_ADD" => "SignerAdd",
            "MESSAGE_TYPE_SIGNER_REMOVE" => "SignerRemove",
            "MESSAGE_TYPE_KEYCHAIN_AUTHORIZE" => "KeychainAuthorize",
            "MESSAGE_TYPE_KEYCHAIN_REVOKE" => "KeychainRevoke",
            "MESSAGE_TYPE_MERGE_REQUEST_ADD" => "MergeRequestAdd",
            "MESSAGE_TYPE_MERGE_REQUEST_REMOVE" => "MergeRequestRemove",
            _ => "Unknown",
        }
    }
}

impl Block {
    /// Borrow the block body. Returns an empty default body if absent
    /// (e.g., a freshly-defaulted `Block` constructed by callers prior to
    /// populating the envelope). Production blocks built via
    /// `makechain_consensus::block::build_block_with_*` always populate
    /// the body — the empty fallback is only there so accessors don't
    /// panic on partially-constructed protos in test fixtures.
    ///
    /// Callers that need the body to definitely be present should
    /// instead use `body.as_option().expect("body")` to assert.
    pub fn body(&self) -> &BlockBody {
        // Manual unwrap_or with a static empty body avoids cloning when
        // the body is present. The static empty body has empty vec/byte
        // fields, which match the legacy "missing payload" sentinels
        // every accessor in the codebase treated as the default.
        static EMPTY: ::std::sync::OnceLock<BlockBody> = ::std::sync::OnceLock::new();
        match self.body.as_option() {
            Some(b) => b,
            None => EMPTY.get_or_init(BlockBody::default),
        }
    }

    /// Mutable accessor for the block body. Populates an empty body if
    /// absent so callers can mutate fields in-place.
    ///
    /// # Panics
    ///
    /// Never panics; the body is always populated in the line above the `expect`.
    pub fn body_mut(&mut self) -> &mut BlockBody {
        if self.body.as_option().is_none() {
            self.body = ::buffa::MessageField::some(BlockBody::default());
        }
        self.body.as_option_mut().expect("body populated above")
    }
}

impl MessageData {
    /// Returns `true` for self-authenticating message bodies that carry their own
    /// authorization (`AccountKeychain` custody signature for `owner_address`) and therefore
    /// bypass the Ed25519-envelope signer pre-check in the mempool submit path.
    ///
    /// Single source of truth — adding a new bypass body type is a one-line
    /// `matches!` extension here instead of an audit-the-codebase exercise.
    /// Today: `SIGNER_ADD`, `SIGNER_REMOVE`, `KEYCHAIN_AUTHORIZE`, `KEYCHAIN_REVOKE`.
    ///
    /// Note: `STORAGE_CLAIM` is self-authenticating in a different sense
    /// (settlement-tx proof) but does not bypass ordinary admission — it has
    /// a separate replay-rejection path via `storage_claim_rejection`. Revisit
    /// before adding it here.
    #[must_use]
    pub fn bypasses_signer_check(&self) -> bool {
        use message_data::Body;
        matches!(
            self.body,
            Some(
                Body::SignerAdd(_)
                    | Body::SignerRemove(_)
                    | Body::KeychainAuthorize(_)
                    | Body::KeychainRevoke(_)
            )
        )
    }
}

/// Duration in seconds for an active storage grant plus grace period
/// (approximately 13 months).
pub const STORAGE_GRANT_TOTAL_SECS: u32 = 395 * 24 * 60 * 60;

/// Canonical protocol version carried in block headers.
pub const PROTOCOL_VERSION: u32 = 0;

/// Maximum parent commits per commit in a `COMMIT_BUNDLE`. Enforced canonically at
/// the codec `read` boundary (`codec.rs`, `values.rs`) AND at message validation
/// (`makechain_state::validation`) so the ingress/buffa path matches the
/// canonical-codec decode bound. Single source of truth (was duplicated as a
/// private const in `makechain-proto` and `makechain-state`).
pub const MAX_PARENT_COMMITS: usize = 64;

/// Maximum accepted validity window for `SIGNER_ADD` / `SIGNER_REMOVE` custody
/// signatures (1 hour). Used by validation and client crates.
pub const MAX_SIGNER_VALIDITY_WINDOW_SECS: u64 = 3600;

/// Canonical `AccountKeychain` length limits, shared across the codec
/// (decode ceilings), crypto (digest input bounds), and state validation
/// (structural caps) so the three layers cannot drift. The TS encoder mirrors
/// these values (`packages/makechain-sdk/src/proto/encode.ts`).
///
/// Maximum custody/signer authorization signature length (unified Tempo-style
/// envelope: secp256k1 / P256 / `WebAuthn` / keychain wrapper).
pub const MAX_KEYCHAIN_SIGNATURE_LEN: usize = 16 * 1024;
/// Maximum explicit custody public key material length (secp256k1 uncompressed
/// SEC1 is the largest at 65 bytes; P256/WebAuthn carry 64).
pub const MAX_KEYCHAIN_PUBLIC_KEY_LEN: usize = 65;
/// Maximum opaque keychain witness (TIP-1053-style challenge binding; inert in
/// v1). Bounds the signed digest input and the stored/wire body.
pub const MAX_KEYCHAIN_WITNESS_LEN: usize = 1024;
/// Maximum `VERIFICATION_ADD` claim signature length.
pub const MAX_CLAIM_SIGNATURE_LEN: usize = 16 * 1024;

/// Maximum `allowed_projects` entries in a scoped `SIGNER_ADD` / keychain authorization.
/// Shared by codec (decode bound), crypto (digest input), and state validation —
/// single source of truth (was duplicated across all three).
pub const MAX_ALLOWED_PROJECTS: usize = 1024;

/// Maximum `WebAuthn` custody envelope length (`DoS` bound on the custody-verify path).
pub const MAX_WEBAUTHN_ENVELOPE_LEN: usize = 2048;

// STORAGE_CLAIM in-band evidence length caps. Bound the variable-length evidence
// on decode (DoS guard) and must stay in sync with the TS canonical encoder + the
// per-block storage-claim cap. Cert ~134 B, header RLP ~1 KB, a receipt can be
// large, and a receipts-trie inclusion proof is shallow (a few nodes).
/// Maximum `STORAGE_CLAIM` finalization-certificate length.
pub const MAX_FINALIZATION_CERT_LEN: usize = 4 * 1024;
/// Maximum `STORAGE_CLAIM` RLP block-header length.
pub const MAX_HEADER_RLP_LEN: usize = 8 * 1024;
/// Maximum `STORAGE_CLAIM` RLP receipt length.
pub const MAX_RECEIPT_RLP_LEN: usize = 256 * 1024;
/// Maximum `STORAGE_CLAIM` receipts-trie inclusion-proof node count.
pub const MAX_PROOF_NODES: usize = 64;
/// Maximum `STORAGE_CLAIM` receipts-trie inclusion-proof node length.
pub const MAX_PROOF_NODE_LEN: usize = 4 * 1024;

// gRPC Server Reflection (`grpc.reflection.v1` + `v1alpha`) wiring on top of
// the generated `DescriptorPool`, via `connectrpc-reflection`.
pub mod reflection;

// Test-only `ServiceRequest` construction for direct service-trait calls.
#[cfg(feature = "test-util")]
pub mod test_util;

/// The full-closure [`DescriptorPool`](buffa_descriptor::DescriptorPool) for
/// the `makechain` package, generated at build time (buffa vtable reflection).
///
/// Backs the gRPC Server Reflection service mounted by
/// [`reflection::install_reflection`] so clients like `grpcurl` and postman
/// can list and describe the service without a separate proto registry.
#[must_use]
pub fn descriptor_pool() -> &'static ::std::sync::Arc<buffa_descriptor::DescriptorPool> {
    generated::makechain::descriptor_pool()
}

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

    #[test]
    fn keychain_message_types_have_distinct_as_str_labels() {
        // Submit metrics label by `as_str`; the keychain types must not collapse
        // into the "Unknown" bucket.
        assert_eq!(
            MessageType::MESSAGE_TYPE_KEYCHAIN_AUTHORIZE.as_str(),
            "KeychainAuthorize"
        );
        assert_eq!(
            MessageType::MESSAGE_TYPE_KEYCHAIN_REVOKE.as_str(),
            "KeychainRevoke"
        );
    }
}
