//! Treasury management for Synor blockchain.
//!
//! The treasury system manages protocol funds with:
//! - Multiple pools (dev fund, ecosystem, reserves)
//! - Spending limits and cooling periods
//! - Multi-layer approval (multisig + DAO)
//! - Automated fund streams

use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use synor_types::{Address, Hash256};

use crate::multisig::{MultisigTxId, MultisigWallet};
use crate::ProposalId;

/// Unique identifier for a treasury pool.
#[derive(
    Clone,
    Copy,
    Debug,
    PartialEq,
    Eq,
    Hash,
    BorshSerialize,
    BorshDeserialize,
    Serialize,
    Deserialize,
)]
pub struct TreasuryPoolId(pub [u8; 32]);

impl TreasuryPoolId {
    /// Creates a new treasury pool ID.
    pub fn new(bytes: [u8; 32]) -> Self {
        TreasuryPoolId(bytes)
    }

    /// Creates from a hash.
    pub fn from_hash(hash: &Hash256) -> Self {
        TreasuryPoolId(*hash.as_bytes())
    }
}

impl std::fmt::Display for TreasuryPoolId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", hex::encode(&self.0[..8]))
    }
}

/// Type of treasury pool.
#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
pub enum TreasuryPoolType {
    /// Development fund for protocol development.
    DevFund,
    /// Ecosystem grants and partnerships.
    Ecosystem,
    /// Future team allocation (locked).
    FutureTeam,
    /// Staking rewards pool.
    StakingRewards,
    /// Strategic reserves.
    Reserves,
    /// Custom pool with description.
    Custom(String),
}

/// Spending limit configuration.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
pub struct SpendingLimit {
    /// Maximum amount per single transaction.
    pub max_per_tx: u64,
    /// Maximum amount per time period.
    pub max_per_period: u64,
    /// Period duration in seconds.
    pub period_seconds: u64,
    /// Amount spent in current period.
    pub spent_this_period: u64,
    /// When current period started.
    pub period_start: u64,
    /// Cooling period between transactions (seconds).
    pub cooling_period: u64,
    /// Last transaction timestamp.
    pub last_tx_time: u64,
}

impl SpendingLimit {
    /// Creates a new spending limit.
    pub fn new(max_per_tx: u64, max_per_period: u64, period_seconds: u64) -> Self {
        SpendingLimit {
            max_per_tx,
            max_per_period,
            period_seconds,
            spent_this_period: 0,
            period_start: 0,
            cooling_period: 0,
            last_tx_time: 0,
        }
    }

    /// Sets cooling period between transactions.
    pub fn with_cooling_period(mut self, seconds: u64) -> Self {
        self.cooling_period = seconds;
        self
    }

    /// Checks if spending is allowed.
    pub fn can_spend(&self, amount: u64, timestamp: u64) -> Result<(), SpendingError> {
        // Check per-tx limit
        if amount > self.max_per_tx {
            return Err(SpendingError::ExceedsPerTxLimit {
                requested: amount,
                max: self.max_per_tx,
            });
        }

        // Check cooling period
        if timestamp < self.last_tx_time + self.cooling_period {
            return Err(SpendingError::CoolingPeriod {
                remaining: (self.last_tx_time + self.cooling_period) - timestamp,
            });
        }

        // Check period limit
        let spent = if timestamp >= self.period_start + self.period_seconds {
            // New period
            0
        } else {
            self.spent_this_period
        };

        if spent + amount > self.max_per_period {
            return Err(SpendingError::ExceedsPeriodLimit {
                requested: amount,
                remaining: self.max_per_period.saturating_sub(spent),
            });
        }

        Ok(())
    }

    /// Records a spending transaction.
    pub fn record_spending(&mut self, amount: u64, timestamp: u64) {
        // Check if we're in a new period
        if timestamp >= self.period_start + self.period_seconds {
            self.period_start = timestamp;
            self.spent_this_period = amount;
        } else {
            self.spent_this_period += amount;
        }
        self.last_tx_time = timestamp;
    }

    /// Remaining allowance in current period.
    pub fn remaining_allowance(&self, timestamp: u64) -> u64 {
        if timestamp >= self.period_start + self.period_seconds {
            self.max_per_period
        } else {
            self.max_per_period.saturating_sub(self.spent_this_period)
        }
    }
}

/// Spending limit presets for different security levels.
impl SpendingLimit {
    /// High security: 1% per tx, 5% per week.
    pub fn high_security(total_balance: u64) -> Self {
        SpendingLimit::new(
            total_balance / 100, // 1% per tx
            total_balance / 20,  // 5% per week
            7 * 24 * 60 * 60,    // 1 week
        )
        .with_cooling_period(24 * 60 * 60) // 1 day cooling
    }

    /// Medium security: 5% per tx, 20% per month.
    pub fn medium_security(total_balance: u64) -> Self {
        SpendingLimit::new(
            total_balance / 20, // 5% per tx
            total_balance / 5,  // 20% per month
            30 * 24 * 60 * 60,  // 1 month
        )
        .with_cooling_period(6 * 60 * 60) // 6 hour cooling
    }

    /// Low security: 10% per tx, 50% per month.
    pub fn low_security(total_balance: u64) -> Self {
        SpendingLimit::new(
            total_balance / 10, // 10% per tx
            total_balance / 2,  // 50% per month
            30 * 24 * 60 * 60,  // 1 month
        )
    }
}

/// Approval requirements for treasury spending.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
pub struct ApprovalRequirements {
    /// Multisig wallet that must approve.
    pub multisig_required: bool,
    /// DAO proposal required above this amount.
    pub dao_threshold: Option<u64>,
    /// Council approval required.
    pub council_required: bool,
    /// Minimum approval delay (seconds).
    pub min_delay: u64,
}

impl Default for ApprovalRequirements {
    fn default() -> Self {
        ApprovalRequirements {
            multisig_required: true,
            dao_threshold: Some(100_000 * 100_000_000), // 100k SYNOR needs DAO
            council_required: false,
            min_delay: 24 * 60 * 60, // 24 hour delay
        }
    }
}

/// Funding stream for continuous payments.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
pub struct FundingStream {
    /// Stream identifier.
    pub id: Hash256,
    /// Recipient address.
    pub recipient: Address,
    /// Total amount to stream.
    pub total_amount: u64,
    /// Amount already claimed.
    pub claimed_amount: u64,
    /// Stream start time.
    pub start_time: u64,
    /// Stream end time.
    pub end_time: u64,
    /// Description of the stream.
    pub description: String,
    /// Whether stream is active.
    pub active: bool,
}

impl FundingStream {
    /// Creates a new funding stream.
    pub fn new(
        id: Hash256,
        recipient: Address,
        total_amount: u64,
        start_time: u64,
        duration: u64,
        description: String,
    ) -> Self {
        FundingStream {
            id,
            recipient,
            total_amount,
            claimed_amount: 0,
            start_time,
            end_time: start_time + duration,
            description,
            active: true,
        }
    }

    /// Calculates claimable amount at given timestamp.
    pub fn claimable(&self, timestamp: u64) -> u64 {
        if !self.active || timestamp < self.start_time {
            return 0;
        }

        let elapsed = if timestamp >= self.end_time {
            self.end_time - self.start_time
        } else {
            timestamp - self.start_time
        };

        let duration = self.end_time - self.start_time;
        let vested = (self.total_amount as u128 * elapsed as u128 / duration as u128) as u64;
        vested.saturating_sub(self.claimed_amount)
    }

    /// Claims available tokens.
    pub fn claim(&mut self, timestamp: u64) -> u64 {
        let claimable = self.claimable(timestamp);
        self.claimed_amount += claimable;
        claimable
    }

    /// Cancels the stream.
    pub fn cancel(&mut self) {
        self.active = false;
    }
}

/// Treasury pool configuration.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
pub struct TreasuryConfig {
    /// Pool type.
    pub pool_type: TreasuryPoolType,
    /// Spending limits.
    pub spending_limit: SpendingLimit,
    /// Approval requirements.
    pub approvals: ApprovalRequirements,
    /// Whether pool can receive deposits.
    pub allow_deposits: bool,
    /// Whether pool is frozen (emergency).
    pub frozen: bool,
}

impl TreasuryConfig {
    /// Creates configuration for dev fund.
    pub fn dev_fund(initial_balance: u64) -> Self {
        TreasuryConfig {
            pool_type: TreasuryPoolType::DevFund,
            spending_limit: SpendingLimit::medium_security(initial_balance),
            approvals: ApprovalRequirements {
                multisig_required: true,
                dao_threshold: Some(500_000 * 100_000_000), // 500k SYNOR
                council_required: false,
                min_delay: 48 * 60 * 60, // 48 hour delay
            },
            allow_deposits: true,
            frozen: false,
        }
    }

    /// Creates configuration for ecosystem fund.
    pub fn ecosystem(initial_balance: u64) -> Self {
        TreasuryConfig {
            pool_type: TreasuryPoolType::Ecosystem,
            spending_limit: SpendingLimit::low_security(initial_balance),
            approvals: ApprovalRequirements {
                multisig_required: true,
                dao_threshold: Some(100_000 * 100_000_000), // 100k SYNOR
                council_required: false,
                min_delay: 24 * 60 * 60,
            },
            allow_deposits: true,
            frozen: false,
        }
    }

    /// Creates configuration for future team allocation.
    pub fn future_team(initial_balance: u64) -> Self {
        TreasuryConfig {
            pool_type: TreasuryPoolType::FutureTeam,
            spending_limit: SpendingLimit::high_security(initial_balance),
            approvals: ApprovalRequirements {
                multisig_required: true,
                dao_threshold: Some(50_000 * 100_000_000), // 50k SYNOR needs DAO
                council_required: true,                    // Council must approve team allocations
                min_delay: 7 * 24 * 60 * 60,               // 7 day delay
            },
            allow_deposits: false, // No external deposits
            frozen: false,
        }
    }

    /// Creates configuration for strategic reserves.
    pub fn reserves(initial_balance: u64) -> Self {
        TreasuryConfig {
            pool_type: TreasuryPoolType::Reserves,
            spending_limit: SpendingLimit::high_security(initial_balance),
            approvals: ApprovalRequirements {
                multisig_required: true,
                dao_threshold: Some(10_000 * 100_000_000), // Any significant spend needs DAO
                council_required: true,
                min_delay: 14 * 24 * 60 * 60, // 14 day delay
            },
            allow_deposits: true,
            frozen: false,
        }
    }
}

/// Spending request state.
#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
pub enum SpendingRequestState {
    /// Awaiting multisig approval.
    PendingMultisig,
    /// Awaiting DAO approval.
    PendingDao,
    /// Awaiting council approval.
    PendingCouncil,
    /// In timelock delay.
    Timelocked { execute_after: u64 },
    /// Ready for execution.
    Ready,
    /// Executed successfully.
    Executed { timestamp: u64 },
    /// Cancelled.
    Cancelled,
    /// Rejected.
    Rejected { reason: String },
}

/// A treasury spending request.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
pub struct SpendingRequest {
    /// Request identifier.
    pub id: Hash256,
    /// Target pool.
    pub pool_id: TreasuryPoolId,
    /// Recipient address.
    pub recipient: Address,
    /// Amount to transfer.
    pub amount: u64,
    /// Description/justification.
    pub description: String,
    /// Current state.
    pub state: SpendingRequestState,
    /// Proposer.
    pub proposer: Address,
    /// Creation timestamp.
    pub created_at: u64,
    /// Associated multisig transaction (if any).
    pub multisig_tx: Option<MultisigTxId>,
    /// Associated DAO proposal (if any).
    pub dao_proposal: Option<ProposalId>,
    /// Council approvals received.
    pub council_approvals: Vec<Address>,
}

/// A treasury pool.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
pub struct TreasuryPool {
    /// Pool identifier.
    pub id: TreasuryPoolId,
    /// Human-readable name.
    pub name: String,
    /// Pool configuration.
    pub config: TreasuryConfig,
    /// Current balance.
    pub balance: u64,
    /// Associated multisig wallet.
    pub multisig_wallet: Option<Hash256>,
    /// Active funding streams.
    pub streams: HashMap<Hash256, FundingStream>,
    /// Pending spending requests.
    pub pending_requests: HashMap<Hash256, SpendingRequest>,
    /// Total deposited all time.
    pub total_deposited: u64,
    /// Total spent all time.
    pub total_spent: u64,
    /// Creation timestamp.
    pub created_at: u64,
}

impl TreasuryPool {
    /// Creates a new treasury pool.
    pub fn new(
        id: TreasuryPoolId,
        name: String,
        config: TreasuryConfig,
        initial_balance: u64,
        timestamp: u64,
    ) -> Self {
        TreasuryPool {
            id,
            name,
            config,
            balance: initial_balance,
            multisig_wallet: None,
            streams: HashMap::new(),
            pending_requests: HashMap::new(),
            total_deposited: initial_balance,
            total_spent: 0,
            created_at: timestamp,
        }
    }

    /// Links a multisig wallet to this pool.
    pub fn set_multisig(&mut self, wallet_id: Hash256) {
        self.multisig_wallet = Some(wallet_id);
    }

    /// Deposits funds into the pool.
    pub fn deposit(&mut self, amount: u64) -> Result<(), TreasuryError> {
        if !self.config.allow_deposits {
            return Err(TreasuryError::DepositsNotAllowed);
        }
        if self.config.frozen {
            return Err(TreasuryError::PoolFrozen);
        }
        self.balance += amount;
        self.total_deposited += amount;
        Ok(())
    }

    /// Creates a spending request.
    pub fn create_spending_request(
        &mut self,
        id: Hash256,
        recipient: Address,
        amount: u64,
        description: String,
        proposer: Address,
        timestamp: u64,
    ) -> Result<&SpendingRequest, TreasuryError> {
        if self.config.frozen {
            return Err(TreasuryError::PoolFrozen);
        }

        if amount > self.balance {
            return Err(TreasuryError::InsufficientBalance {
                requested: amount,
                available: self.balance,
            });
        }

        // Check spending limits
        self.config
            .spending_limit
            .can_spend(amount, timestamp)
            .map_err(TreasuryError::SpendingLimitExceeded)?;

        // Determine initial state based on requirements
        let initial_state =
            if self.config.approvals.multisig_required && self.multisig_wallet.is_some() {
                SpendingRequestState::PendingMultisig
            } else if self
                .config
                .approvals
                .dao_threshold
                .is_some_and(|t| amount >= t)
            {
                SpendingRequestState::PendingDao
            } else if self.config.approvals.council_required {
                SpendingRequestState::PendingCouncil
            } else {
                SpendingRequestState::Timelocked {
                    execute_after: timestamp + self.config.approvals.min_delay,
                }
            };

        let request = SpendingRequest {
            id,
            pool_id: self.id,
            recipient,
            amount,
            description,
            state: initial_state,
            proposer,
            created_at: timestamp,
            multisig_tx: None,
            dao_proposal: None,
            council_approvals: Vec::new(),
        };

        self.pending_requests.insert(id, request);
        Ok(self.pending_requests.get(&id).unwrap())
    }

    /// Records multisig approval for a request.
    pub fn approve_multisig(
        &mut self,
        request_id: &Hash256,
        multisig_tx: MultisigTxId,
        timestamp: u64,
    ) -> Result<(), TreasuryError> {
        let request = self
            .pending_requests
            .get_mut(request_id)
            .ok_or(TreasuryError::RequestNotFound)?;

        if request.state != SpendingRequestState::PendingMultisig {
            return Err(TreasuryError::InvalidState);
        }

        request.multisig_tx = Some(multisig_tx);

        // Move to next approval stage
        let needs_dao = self
            .config
            .approvals
            .dao_threshold
            .is_some_and(|t| request.amount >= t);

        if needs_dao {
            request.state = SpendingRequestState::PendingDao;
        } else if self.config.approvals.council_required {
            request.state = SpendingRequestState::PendingCouncil;
        } else {
            request.state = SpendingRequestState::Timelocked {
                execute_after: timestamp + self.config.approvals.min_delay,
            };
        }

        Ok(())
    }

    /// Records DAO approval for a request.
    pub fn approve_dao(
        &mut self,
        request_id: &Hash256,
        proposal_id: ProposalId,
        timestamp: u64,
    ) -> Result<(), TreasuryError> {
        let request = self
            .pending_requests
            .get_mut(request_id)
            .ok_or(TreasuryError::RequestNotFound)?;

        if request.state != SpendingRequestState::PendingDao {
            return Err(TreasuryError::InvalidState);
        }

        request.dao_proposal = Some(proposal_id);

        if self.config.approvals.council_required {
            request.state = SpendingRequestState::PendingCouncil;
        } else {
            request.state = SpendingRequestState::Timelocked {
                execute_after: timestamp + self.config.approvals.min_delay,
            };
        }

        Ok(())
    }

    /// Records council approval for a request.
    pub fn approve_council(
        &mut self,
        request_id: &Hash256,
        council_member: Address,
        required_approvals: usize,
        timestamp: u64,
    ) -> Result<(), TreasuryError> {
        let request = self
            .pending_requests
            .get_mut(request_id)
            .ok_or(TreasuryError::RequestNotFound)?;

        if request.state != SpendingRequestState::PendingCouncil {
            return Err(TreasuryError::InvalidState);
        }

        if !request.council_approvals.contains(&council_member) {
            request.council_approvals.push(council_member);
        }

        if request.council_approvals.len() >= required_approvals {
            request.state = SpendingRequestState::Timelocked {
                execute_after: timestamp + self.config.approvals.min_delay,
            };
        }

        Ok(())
    }

    /// Executes an approved spending request.
    pub fn execute_request(
        &mut self,
        request_id: &Hash256,
        timestamp: u64,
    ) -> Result<SpendingRequest, TreasuryError> {
        let request = self
            .pending_requests
            .get(request_id)
            .ok_or(TreasuryError::RequestNotFound)?;

        // Check if ready
        match &request.state {
            SpendingRequestState::Ready => {}
            SpendingRequestState::Timelocked { execute_after } => {
                if timestamp < *execute_after {
                    return Err(TreasuryError::TimelockActive {
                        remaining: execute_after - timestamp,
                    });
                }
            }
            _ => return Err(TreasuryError::InvalidState),
        }

        let amount = request.amount;

        // Double-check balance
        if amount > self.balance {
            return Err(TreasuryError::InsufficientBalance {
                requested: amount,
                available: self.balance,
            });
        }

        // Execute
        self.balance -= amount;
        self.total_spent += amount;
        self.config
            .spending_limit
            .record_spending(amount, timestamp);

        // Update request state and remove from pending
        let mut executed_request = self.pending_requests.remove(request_id).unwrap();
        executed_request.state = SpendingRequestState::Executed { timestamp };

        Ok(executed_request)
    }

    /// Cancels a pending request.
    pub fn cancel_request(
        &mut self,
        request_id: &Hash256,
        canceller: &Address,
    ) -> Result<(), TreasuryError> {
        let request = self
            .pending_requests
            .get_mut(request_id)
            .ok_or(TreasuryError::RequestNotFound)?;

        // Only proposer can cancel
        if &request.proposer != canceller {
            return Err(TreasuryError::NotAuthorized);
        }

        // Can't cancel executed requests
        if matches!(request.state, SpendingRequestState::Executed { .. }) {
            return Err(TreasuryError::InvalidState);
        }

        request.state = SpendingRequestState::Cancelled;
        Ok(())
    }

    /// Creates a funding stream.
    pub fn create_stream(
        &mut self,
        id: Hash256,
        recipient: Address,
        amount: u64,
        start_time: u64,
        duration: u64,
        description: String,
    ) -> Result<&FundingStream, TreasuryError> {
        if self.config.frozen {
            return Err(TreasuryError::PoolFrozen);
        }

        if amount > self.balance {
            return Err(TreasuryError::InsufficientBalance {
                requested: amount,
                available: self.balance,
            });
        }

        // Reserve the amount
        self.balance -= amount;

        let stream = FundingStream::new(id, recipient, amount, start_time, duration, description);

        self.streams.insert(id, stream);
        Ok(self.streams.get(&id).unwrap())
    }

    /// Claims from a funding stream.
    pub fn claim_stream(
        &mut self,
        stream_id: &Hash256,
        claimer: &Address,
        timestamp: u64,
    ) -> Result<u64, TreasuryError> {
        let stream = self
            .streams
            .get_mut(stream_id)
            .ok_or(TreasuryError::StreamNotFound)?;

        if &stream.recipient != claimer {
            return Err(TreasuryError::NotAuthorized);
        }

        let claimed = stream.claim(timestamp);
        self.total_spent += claimed;

        Ok(claimed)
    }

    /// Cancels a funding stream (returns unvested to pool).
    pub fn cancel_stream(&mut self, stream_id: &Hash256) -> Result<u64, TreasuryError> {
        let stream = self
            .streams
            .get_mut(stream_id)
            .ok_or(TreasuryError::StreamNotFound)?;

        let remaining = stream.total_amount - stream.claimed_amount;
        stream.cancel();

        // Return unvested amount to pool
        self.balance += remaining;

        Ok(remaining)
    }

    /// Freezes the pool (emergency).
    pub fn freeze(&mut self) {
        self.config.frozen = true;
    }

    /// Unfreezes the pool.
    pub fn unfreeze(&mut self) {
        self.config.frozen = false;
    }
}

/// Main treasury system managing all pools.
#[derive(Clone, Debug)]
pub struct Treasury {
    /// All treasury pools.
    pools: HashMap<TreasuryPoolId, TreasuryPool>,
    /// Associated multisig wallets.
    multisig_wallets: HashMap<Hash256, MultisigWallet>,
    /// Guardian address (emergency powers).
    guardian: Option<Address>,
    /// Council members.
    council: Vec<Address>,
}

impl Treasury {
    /// Creates a new treasury system.
    pub fn new() -> Self {
        Treasury {
            pools: HashMap::new(),
            multisig_wallets: HashMap::new(),
            guardian: None,
            council: Vec::new(),
        }
    }

    /// Sets the guardian address.
    pub fn set_guardian(&mut self, guardian: Address) {
        self.guardian = Some(guardian);
    }

    /// Sets the council.
    pub fn set_council(&mut self, council: Vec<Address>) {
        self.council = council;
    }

    /// Returns the council.
    pub fn council(&self) -> &[Address] {
        &self.council
    }

    /// Adds a pool to the treasury.
    pub fn add_pool(&mut self, pool: TreasuryPool) -> TreasuryPoolId {
        let id = pool.id;
        self.pools.insert(id, pool);
        id
    }

    /// Gets a pool by ID.
    pub fn get_pool(&self, id: &TreasuryPoolId) -> Option<&TreasuryPool> {
        self.pools.get(id)
    }

    /// Gets a mutable pool by ID.
    pub fn get_pool_mut(&mut self, id: &TreasuryPoolId) -> Option<&mut TreasuryPool> {
        self.pools.get_mut(id)
    }

    /// Gets all pools.
    pub fn pools(&self) -> impl Iterator<Item = &TreasuryPool> {
        self.pools.values()
    }

    /// Total balance across all pools.
    pub fn total_balance(&self) -> u64 {
        self.pools.values().map(|p| p.balance).sum()
    }

    /// Links a multisig wallet to a pool.
    pub fn link_multisig(
        &mut self,
        pool_id: &TreasuryPoolId,
        wallet: MultisigWallet,
    ) -> Result<(), TreasuryError> {
        let wallet_id = wallet.id;

        let pool = self
            .pools
            .get_mut(pool_id)
            .ok_or(TreasuryError::PoolNotFound)?;

        pool.set_multisig(wallet_id);
        self.multisig_wallets.insert(wallet_id, wallet);

        Ok(())
    }

    /// Emergency freeze all pools.
    pub fn emergency_freeze(&mut self, caller: &Address) -> Result<(), TreasuryError> {
        if self.guardian.as_ref() != Some(caller) {
            return Err(TreasuryError::NotAuthorized);
        }

        for pool in self.pools.values_mut() {
            pool.freeze();
        }

        Ok(())
    }

    /// Emergency unfreeze all pools.
    pub fn emergency_unfreeze(&mut self, caller: &Address) -> Result<(), TreasuryError> {
        if self.guardian.as_ref() != Some(caller) {
            return Err(TreasuryError::NotAuthorized);
        }

        for pool in self.pools.values_mut() {
            pool.unfreeze();
        }

        Ok(())
    }

    /// Creates the standard genesis treasury pools.
    pub fn create_genesis_pools(timestamp: u64) -> Self {
        let mut treasury = Treasury::new();

        // Constants for distribution (with 8 decimals)
        const UNIT: u64 = 100_000_000;

        // Dev Fund: 10.5M SYNOR (15%)
        let dev_fund_amount = 10_500_000 * UNIT;
        let dev_fund_id = TreasuryPoolId::new([1u8; 32]);
        let dev_fund = TreasuryPool::new(
            dev_fund_id,
            "Development Fund".to_string(),
            TreasuryConfig::dev_fund(dev_fund_amount),
            dev_fund_amount,
            timestamp,
        );
        treasury.add_pool(dev_fund);

        // Ecosystem: 7M SYNOR (10%)
        let ecosystem_amount = 7_000_000 * UNIT;
        let ecosystem_id = TreasuryPoolId::new([2u8; 32]);
        let ecosystem = TreasuryPool::new(
            ecosystem_id,
            "Ecosystem Fund".to_string(),
            TreasuryConfig::ecosystem(ecosystem_amount),
            ecosystem_amount,
            timestamp,
        );
        treasury.add_pool(ecosystem);

        // Future Team: 3.5M SYNOR (5%)
        let team_amount = 3_500_000 * UNIT;
        let team_id = TreasuryPoolId::new([3u8; 32]);
        let team = TreasuryPool::new(
            team_id,
            "Future Team Allocation".to_string(),
            TreasuryConfig::future_team(team_amount),
            team_amount,
            timestamp,
        );
        treasury.add_pool(team);

        // Staking Rewards: 7M SYNOR (10%)
        let staking_amount = 7_000_000 * UNIT;
        let staking_id = TreasuryPoolId::new([4u8; 32]);
        let staking = TreasuryPool::new(
            staking_id,
            "Staking Rewards".to_string(),
            TreasuryConfig {
                pool_type: TreasuryPoolType::StakingRewards,
                spending_limit: SpendingLimit::low_security(staking_amount),
                approvals: ApprovalRequirements {
                    multisig_required: false, // Automated distribution
                    dao_threshold: None,
                    council_required: false,
                    min_delay: 0,
                },
                allow_deposits: true, // Mining fees can be deposited
                frozen: false,
            },
            staking_amount,
            timestamp,
        );
        treasury.add_pool(staking);

        // Reserves: 3.5M SYNOR (5%)
        let reserve_amount = 3_500_000 * UNIT;
        let reserve_id = TreasuryPoolId::new([5u8; 32]);
        let reserves = TreasuryPool::new(
            reserve_id,
            "Strategic Reserves".to_string(),
            TreasuryConfig::reserves(reserve_amount),
            reserve_amount,
            timestamp,
        );
        treasury.add_pool(reserves);

        treasury
    }
}

impl Default for Treasury {
    fn default() -> Self {
        Self::new()
    }
}

/// Spending errors.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SpendingError {
    /// Amount exceeds per-transaction limit.
    ExceedsPerTxLimit { requested: u64, max: u64 },
    /// Cooling period not elapsed.
    CoolingPeriod { remaining: u64 },
    /// Amount exceeds period limit.
    ExceedsPeriodLimit { requested: u64, remaining: u64 },
}

impl std::fmt::Display for SpendingError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ExceedsPerTxLimit { requested, max } => {
                write!(f, "Requested {} exceeds per-tx limit of {}", requested, max)
            }
            Self::CoolingPeriod { remaining } => {
                write!(f, "Cooling period: {} seconds remaining", remaining)
            }
            Self::ExceedsPeriodLimit {
                requested,
                remaining,
            } => {
                write!(
                    f,
                    "Requested {} exceeds remaining period allowance of {}",
                    requested, remaining
                )
            }
        }
    }
}

impl std::error::Error for SpendingError {}

/// Treasury errors.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TreasuryError {
    /// Pool not found.
    PoolNotFound,
    /// Pool is frozen.
    PoolFrozen,
    /// Deposits not allowed.
    DepositsNotAllowed,
    /// Insufficient balance.
    InsufficientBalance { requested: u64, available: u64 },
    /// Spending limit exceeded.
    SpendingLimitExceeded(SpendingError),
    /// Request not found.
    RequestNotFound,
    /// Stream not found.
    StreamNotFound,
    /// Invalid state for operation.
    InvalidState,
    /// Timelock still active.
    TimelockActive { remaining: u64 },
    /// Not authorized.
    NotAuthorized,
}

impl std::fmt::Display for TreasuryError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::PoolNotFound => write!(f, "Treasury pool not found"),
            Self::PoolFrozen => write!(f, "Treasury pool is frozen"),
            Self::DepositsNotAllowed => write!(f, "Deposits not allowed for this pool"),
            Self::InsufficientBalance {
                requested,
                available,
            } => {
                write!(
                    f,
                    "Insufficient balance: {} requested, {} available",
                    requested, available
                )
            }
            Self::SpendingLimitExceeded(e) => write!(f, "Spending limit exceeded: {}", e),
            Self::RequestNotFound => write!(f, "Spending request not found"),
            Self::StreamNotFound => write!(f, "Funding stream not found"),
            Self::InvalidState => write!(f, "Invalid state for this operation"),
            Self::TimelockActive { remaining } => {
                write!(f, "Timelock active: {} seconds remaining", remaining)
            }
            Self::NotAuthorized => write!(f, "Not authorized"),
        }
    }
}

impl std::error::Error for TreasuryError {}

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

    fn test_address(n: u8) -> Address {
        let mut bytes = [0u8; 32];
        bytes[0] = n;
        Address::from_parts(
            synor_types::Network::Devnet,
            synor_types::address::AddressType::P2PKH,
            bytes,
        )
    }

    fn test_hash(n: u8) -> Hash256 {
        let mut bytes = [0u8; 32];
        bytes[0] = n;
        Hash256::from_bytes(bytes)
    }

    #[test]
    fn test_spending_limit() {
        let mut limit = SpendingLimit::new(
            1000, // 1000 per tx
            5000, // 5000 per period
            3600, // 1 hour period
        );

        // Should allow first spend
        assert!(limit.can_spend(500, 100).is_ok());
        limit.record_spending(500, 100);

        // Should allow second spend
        assert!(limit.can_spend(500, 200).is_ok());
        limit.record_spending(500, 200);

        // Should reject exceeding per-tx limit
        assert!(limit.can_spend(1500, 300).is_err());

        // Should track period spending
        assert_eq!(limit.remaining_allowance(300), 4000);

        // Should reset in new period
        assert_eq!(limit.remaining_allowance(4000), 5000);
    }

    #[test]
    fn test_treasury_pool() {
        let pool_id = TreasuryPoolId::new([1u8; 32]);
        let config = TreasuryConfig::ecosystem(1_000_000);
        let mut pool = TreasuryPool::new(pool_id, "Test Pool".to_string(), config, 1_000_000, 0);

        // Create spending request
        let request_id = test_hash(1);
        let recipient = test_address(1);
        let proposer = test_address(2);

        pool.create_spending_request(
            request_id,
            recipient,
            10_000,
            "Test payment".to_string(),
            proposer,
            100,
        )
        .unwrap();

        assert!(pool.pending_requests.contains_key(&request_id));
    }

    #[test]
    fn test_funding_stream() {
        let mut stream = FundingStream::new(
            test_hash(1),
            test_address(1),
            12000, // 12000 tokens
            0,     // start at 0
            1200,  // 1200 seconds duration (10 tokens/sec)
            "Test stream".to_string(),
        );

        // At start, nothing claimable
        assert_eq!(stream.claimable(0), 0);

        // At 600 seconds, half is vested
        assert_eq!(stream.claimable(600), 6000);

        // Claim partial
        assert_eq!(stream.claim(300), 3000);
        assert_eq!(stream.claimed_amount, 3000);

        // Claimable updates
        assert_eq!(stream.claimable(600), 3000);

        // After end, all vested
        assert_eq!(stream.claimable(2000), 9000); // 12000 - 3000 already claimed
    }

    #[test]
    fn test_genesis_pools() {
        let treasury = Treasury::create_genesis_pools(0);

        // Should have 5 pools
        assert_eq!(treasury.pools.len(), 5);

        // Total should be 31.5M (non-mining allocation minus founder)
        const UNIT: u64 = 100_000_000;
        let expected_total = 31_500_000 * UNIT;
        assert_eq!(treasury.total_balance(), expected_total);
    }
}
