use steel::*;

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
pub enum GovernanceInstruction {
    Initialize = 0,
    CreateProposal = 1,
    CreateProposalWithSession = 10,
    Vote = 2,
    VoteWithSession = 11,
    CancelProposal = 3,
    FinalizeProposal = 4,  // Move from Active to Passed/Failed after voting ends
    SetOilProgramId = 5,   // Admin-only: Update OIL program ID
    SetExecutor = 6,       // Admin-only: Update executor address
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Initialize {
    pub oil_program_id: [u8; 32],
    pub oil_mint: [u8; 32],
    pub voting_period_seconds: [u8; 8],
    pub timelock_seconds: [u8; 8],
    pub quorum_percentage_bps: [u8; 8],
    pub threshold_bps: [u8; 8],  // Single threshold for all proposals
    pub min_stake_duration_seconds: [u8; 8],  // Minimum stake duration before voting (default: 7 days = 604800)
    pub executor: [u8; 32],  // Executor address (can finalize proposals after voting ends)
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct CreateProposal {
    pub description_hash: [u8; 32],
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Vote {
    pub proposal_id: [u8; 8],
    pub choice: u8,  // VoteChoice as u8
}


#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct CancelProposal {
    pub proposal_id: [u8; 8],
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct FinalizeProposal {
    pub proposal_id: [u8; 8],
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct SetOilProgramId {
    pub oil_program_id: [u8; 32],
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct SetExecutor {
    pub executor: [u8; 32],
}

// Helper trait for instruction serialization
pub trait TryFromBytes: Sized {
    fn try_from_bytes(data: &[u8]) -> Result<Self, ProgramError>;
}

impl TryFromBytes for Initialize {
    fn try_from_bytes(data: &[u8]) -> Result<Self, ProgramError> {
        if data.len() < std::mem::size_of::<Self>() {
            return Err(ProgramError::InvalidInstructionData);
        }
        Ok(unsafe { std::ptr::read(data.as_ptr() as *const Self) })
    }
}

impl TryFromBytes for CreateProposal {
    fn try_from_bytes(data: &[u8]) -> Result<Self, ProgramError> {
        if data.len() < std::mem::size_of::<Self>() {
            return Err(ProgramError::InvalidInstructionData);
        }
        Ok(unsafe { std::ptr::read(data.as_ptr() as *const Self) })
    }
}

impl TryFromBytes for Vote {
    fn try_from_bytes(data: &[u8]) -> Result<Self, ProgramError> {
        if data.len() < std::mem::size_of::<Self>() {
            return Err(ProgramError::InvalidInstructionData);
        }
        Ok(unsafe { std::ptr::read(data.as_ptr() as *const Self) })
    }
}

impl TryFromBytes for CancelProposal {
    fn try_from_bytes(data: &[u8]) -> Result<Self, ProgramError> {
        if data.len() < std::mem::size_of::<Self>() {
            return Err(ProgramError::InvalidInstructionData);
        }
        Ok(unsafe { std::ptr::read(data.as_ptr() as *const Self) })
    }
}

impl TryFromBytes for FinalizeProposal {
    fn try_from_bytes(data: &[u8]) -> Result<Self, ProgramError> {
        if data.len() < std::mem::size_of::<Self>() {
            return Err(ProgramError::InvalidInstructionData);
        }
        Ok(unsafe { std::ptr::read(data.as_ptr() as *const Self) })
    }
}

impl TryFromBytes for SetOilProgramId {
    fn try_from_bytes(data: &[u8]) -> Result<Self, ProgramError> {
        if data.len() < std::mem::size_of::<Self>() {
            return Err(ProgramError::InvalidInstructionData);
        }
        Ok(unsafe { std::ptr::read(data.as_ptr() as *const Self) })
    }
}

impl TryFromBytes for SetExecutor {
    fn try_from_bytes(data: &[u8]) -> Result<Self, ProgramError> {
        if data.len() < std::mem::size_of::<Self>() {
            return Err(ProgramError::InvalidInstructionData);
        }
        Ok(unsafe { std::ptr::read(data.as_ptr() as *const Self) })
    }
}
