use serde::{Deserialize, Serialize};
use steel::*;

use crate::state::proposal_pda;
use super::GovernanceAccount;

/// Proposal account (one per proposal)
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Proposal {
    /// Unique proposal ID
    pub id: u64,
    
    /// Creator of the proposal
    pub creator: Pubkey,
    
    /// Deposit amount (refunded if passes, burned if fails)
    pub deposit: u64,
    
    /// Timestamp when proposal was created
    pub created_at: u64,
    
    /// Timestamp when voting ends
    pub voting_ends_at: u64,
    
    /// Timestamp when proposal can be executed (after timelock)
    pub executable_at: u64,
    
    /// Timestamp when proposal was executed (None if not executed)
    pub executed_at: u64,  // 0 if not executed
    
    /// Current status of the proposal (as u8)
    pub status: u8,
    
    /// Buffer field for future use (includes padding for alignment)
    pub buffer_status: [u8; 7],  // 7 bytes padding + future use
    
    /// Hash of proposal description (IPFS hash or similar)
    pub description_hash: [u8; 32],
    
    /// Total yes votes (in stake score units)
    pub yes_votes: u64,
    
    /// Total no votes (in stake score units)
    pub no_votes: u64,
    
    /// Total abstain votes (in stake score units)
    pub abstain_votes: u64,
    
    /// Buffer field (formerly tier, not used - all proposals use same threshold)
    pub buffer_a: u8,
    
    /// Buffer field for future use (includes padding for alignment)
    pub buffer_b: [u8; 7],  // 7 bytes padding + future use
    
    /// Snapshot of total_staked_score when proposal was created (for quorum calculation)
    pub snapshot_total_staked_score: u64,
    
    /// Proposal title (first 8 bytes of title hash or identifier)
    /// Full title is stored in database/IPFS, this is for on-chain reference
    pub proposal_title: u64,
    
    /// Buffer field for future use
    pub buffer_c: u64,
}

impl Proposal {
    pub fn pda(id: u64) -> (Pubkey, u8) {
        proposal_pda(id)
    }
    
    /// Get status as enum
    pub fn status(&self) -> Result<ProposalStatus, ProgramError> {
        ProposalStatus::try_from(self.status)
            .map_err(|_| ProgramError::InvalidAccountData)
    }
    
    /// Check if proposal is active (can be voted on)
    pub fn is_active(&self, current_time: u64) -> bool {
        matches!(self.status(), Ok(ProposalStatus::Active)) && current_time < self.voting_ends_at
    }
    
    /// Check if proposal can be executed
    pub fn can_execute(&self, current_time: u64) -> bool {
        matches!(self.status(), Ok(ProposalStatus::Passed)) && current_time >= self.executable_at
    }
    
    /// Get total votes cast
    pub fn total_votes(&self) -> u64 {
        self.yes_votes + self.no_votes + self.abstain_votes
    }
    
    /// Check if quorum is met
    pub fn meets_quorum(&self, quorum_threshold: u64) -> bool {
        self.total_votes() >= quorum_threshold
    }
    
    /// Check if threshold is met (for yes votes)
    pub fn meets_threshold(&self, threshold_bps: u64) -> bool {
        let total_voting = self.yes_votes + self.no_votes; // Abstain doesn't count
        if total_voting == 0 {
            return false;
        }
        let yes_percentage = (self.yes_votes * 10_000) / total_voting;
        yes_percentage > threshold_bps
    }
}

/// Proposal status
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive, Serialize, Deserialize)]
pub enum ProposalStatus {
    Active = 0,      // Currently in voting period
    Passed = 1,      // Voting passed, waiting for timelock
    Failed = 2,      // Voting failed (quorum/threshold not met)
    Executed = 3,     // Proposal has been executed
    Cancelled = 4,   // Proposal was cancelled
}

account!(GovernanceAccount, Proposal);
