// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Feature Management Program Interface
//!
//! This program provides a system-level mechanism for managing feature flags.
//! Activation is presence-based and append-only: a feature is active once its
//! name is added to the on-chain set, and names are never removed.

#![no_std]

extern crate alloc;

use alloc::vec::Vec;

use rialo_s_pubkey::Pubkey;

pub mod error;
pub mod features;
pub mod instruction;
pub mod state;

rialo_s_pubkey::declare_id!("Feature1111111111111111111111111111111111111");

/// Storage account PDA seed for the features map
pub const STORAGE_ACCOUNT_SEED: &[u8] = b"features_storage";

/// Derive the storage account address
pub fn get_storage_account_address() -> (rialo_s_pubkey::Pubkey, u8) {
    rialo_s_pubkey::Pubkey::find_program_address(&[STORAGE_ACCOUNT_SEED], &id())
}

/// Maximum length for a feature name
pub const MAX_FEATURE_NAME_LENGTH: usize = 512;

/// Maximum number of features allowed in the system
pub const MAX_FEATURE_COUNT: usize = 10000;

/// Maximum number of names a single `Enable` instruction may carry.
///
/// Bounded by the ~64 KB transaction-size limit: 100 × 512-byte names
/// (`MAX_FEATURE_NAME_LENGTH`) plus borsh length prefixes ≈ 51.6 KB, well
/// inside the envelope with comfortable room for transaction headers,
/// signatures, and other instructions in the same tx. Raising the cap
/// requires re-validating the worst-case payload against the tx-size limit.
pub const MAX_NAMES_PER_BATCH: usize = 100;

/// Maximum total size of serialized features state (100 KB)
pub const MAX_FEATURES_STATE_SIZE: usize = 100 * 1024;

/// Maximum number of outstanding `ScheduleEnable` requests in the pending set.
///
/// Bounds the on-chain pending map (and its share of `MAX_FEATURES_STATE_SIZE`)
/// and the number of live one-shot subscriptions the program can have
/// registered at once.
pub const MAX_PENDING_REQUESTS: usize = 100;

/// Maximum lead time for a `ScheduleEnable`, in milliseconds (~100 years).
///
/// `fire_at_ms` must be in the future and within this horizon of the current
/// block time. Mirrors the sanity bound the windowed-era schema enforced —
/// catches fat-fingered far-future timestamps (e.g. seconds-vs-millis) that
/// would otherwise pin a subscription effectively forever.
pub const MAX_SCHEDULE_HORIZON_MS: u64 = 100 * 365 * 24 * 60 * 60 * 1000;

/// Validates a feature name
///
/// Feature names must:
/// - Not be empty
/// - Not exceed MAX_FEATURE_NAME_LENGTH
/// - Contain only alphanumeric characters, underscores, and hyphens
/// - Not start or end with whitespace
pub fn validate_feature_name(name: &str) -> bool {
    !name.is_empty()
        && name.len() <= MAX_FEATURE_NAME_LENGTH
        && name
            .chars()
            .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
        && !name.starts_with(char::is_whitespace)
        && !name.ends_with(char::is_whitespace)
}

/// Create genesis storage data with initial authority
///
/// Creates the initial FeaturesState with the provided authority and serializes it
/// for use in genesis configuration.
///
/// # Arguments
/// * `authority` - The pubkey that will have authority over the feature management system
///
/// # Returns
/// A `Vec<u8>` containing the serialized FeaturesState
pub fn genesis_storage(authority: Pubkey) -> Vec<u8> {
    use state::FeaturesState;

    let state = FeaturesState::new(authority);
    state
        .serialize()
        .expect("Failed to serialize genesis state")
}
