Documentation
use arrayref::{array_ref, array_refs};
use solana_program::{
    instruction::AccountMeta, instruction::Instruction, program_error::ProgramError, pubkey::Pubkey,
};

use crate::state;

pub enum YesNoInstruction<'a> {
    CreateQuestion { name: &'a str, deadline: u64 },
    Bet { yesno: u8 },
    CloseQuestion,
}

impl<'a> YesNoInstruction<'a> {
    pub fn unpack(input: &'a [u8]) -> Result<Self, ProgramError> {
        use ProgramError::InvalidInstructionData;
        let (&tag, rest) = input.split_first().ok_or(InvalidInstructionData)?;

        Ok(match tag {
            0 => {
                let rest = array_ref!(rest, 0, 264);
                let (name, deadline) = array_refs!(rest, 256, 8);
                let name = state::unpack_str(name).unwrap();
                let deadline = u64::from_le_bytes(*deadline);
                Self::CreateQuestion { name, deadline }
            }
            1 => {
                let yesno = rest[0];
                Self::Bet { yesno }
            }
            2 => {
                // let yesno = rest[0];
                Self::CloseQuestion
            }
            _ => return Err(InvalidInstructionData),
        })
    }

    pub fn pack(&self) -> Vec<u8> {
        let mut buf: Vec<u8> = Vec::with_capacity(100);
        match *self {
            Self::CreateQuestion { name, deadline } => {
                buf.push(0);
                let mut name_bytes: [u8; 256] = [0; 256];
                state::pack_str(name, &mut name_bytes).unwrap();
                buf.extend_from_slice(&name_bytes);
                buf.extend_from_slice(&deadline.to_le_bytes());
            }
            Self::Bet { yesno } => {
                buf.push(1);
                buf.push(yesno);
            }
            Self::CloseQuestion {} => {
                buf.push(2);
                // buf.push(yesno);
            }
        }
        buf
    }
}

pub fn create_question(
    program_id: &Pubkey,
    question: &Pubkey,
    creator: &Pubkey,
    mint: &Pubkey,
    creator_spl: &Pubkey,
    mint_owner: &Pubkey,
    name: &str,
    deadline: u64,
) -> Result<Instruction, ProgramError> {
    let data = YesNoInstruction::CreateQuestion { name, deadline }.pack();
    let accounts = vec![
        AccountMeta::new(*question, false),
        AccountMeta::new(*creator, true),
        AccountMeta::new(*mint, false),
        AccountMeta::new(*mint, false),
        AccountMeta::new(*creator_spl, false),
        AccountMeta::new(*mint_owner, true),
        AccountMeta::new_readonly(spl_token::id(), false),
    ];
    Ok(Instruction {
        program_id: *program_id,
        accounts,
        data,
    })
}

pub fn create_bet(
    program_id: &Pubkey,
    bet: &Pubkey,
    question: &Pubkey,
    bet_maker: &Pubkey,
    yesno: u8,
) -> Result<Instruction, ProgramError> {
    let data = YesNoInstruction::Bet { yesno }.pack();
    let accounts = vec![
        AccountMeta::new(*bet, false),
        AccountMeta::new(*question, false),
        AccountMeta::new(*bet_maker, true),
    ];
    Ok(Instruction {
        program_id: *program_id,
        accounts,
        data,
    })
}