use {
    cosmwasm_std::{
        Addr,
        Binary,
        Coin,
        StdResult,
        Storage,
    },
    cosmwasm_storage::{
        bucket,
        bucket_read,
        singleton,
        singleton_read,
        Bucket,
        ReadonlyBucket,
        ReadonlySingleton,
        Singleton,
    },
    pyth_sdk_cw::PriceFeed,
    schemars::JsonSchema,
    serde::{
        Deserialize,
        Serialize,
    },
    std::{
        collections::HashSet,
        time::Duration,
    },
};

pub static CONFIG_KEY: &[u8] = b"config_v1";
pub static PRICE_FEED_KEY: &[u8] = b"price_feed";
pub static CONTRACT_VERSION_KEY: &[u8] = b"contract_version";

/// A `PythDataSource` identifies a specific contract (given by its Wormhole `emitter`) on
/// a specific blockchain (given by `chain_id`).
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, JsonSchema)]
pub struct PythDataSource {
    pub emitter:  Binary,
    pub chain_id: u16,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct ConfigInfo {
    pub wormhole_contract:          Addr,
    pub data_sources:               HashSet<PythDataSource>,
    pub governance_source:          PythDataSource,
    // Index for preventing replay attacks on governance data source transfers.
    // This index increases every time the governance data source is changed, which prevents old
    // transfer request VAAs from being replayed.
    pub governance_source_index:    u32,
    // The wormhole sequence number for governance messages. This value is increased every time the
    // a governance instruction is executed.
    //
    // This field differs from the one above in that it is generated by wormhole and applicable to all
    // governance messages, whereas the one above is generated by Pyth and only applicable to governance
    // source transfers.
    pub governance_sequence_number: u64,
    // Warning: This id needs to agree with the wormhole chain id.
    // We should read this directly from wormhole, but their contract doesn't expose it.
    pub chain_id:                   u16,
    pub valid_time_period:          Duration,

    // The fee to pay, denominated in fee_denom (typically, the chain's native token)
    pub fee: Coin,
}

pub fn config(storage: &mut dyn Storage) -> Singleton<ConfigInfo> {
    singleton(storage, CONFIG_KEY)
}

pub fn config_read(storage: &dyn Storage) -> ReadonlySingleton<ConfigInfo> {
    singleton_read(storage, CONFIG_KEY)
}

pub fn price_feed_bucket(storage: &mut dyn Storage) -> Bucket<PriceFeed> {
    bucket(storage, PRICE_FEED_KEY)
}

pub fn price_feed_read_bucket(storage: &dyn Storage) -> ReadonlyBucket<PriceFeed> {
    bucket_read(storage, PRICE_FEED_KEY)
}

pub fn set_contract_version(storage: &mut dyn Storage, contract_version: &String) -> StdResult<()> {
    singleton(storage, CONTRACT_VERSION_KEY).save(contract_version)
}

pub fn get_contract_version(storage: &mut dyn Storage) -> StdResult<String> {
    singleton_read(storage, CONTRACT_VERSION_KEY).load()
}
