Documentation
use borsh::BorshSerialize;
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    program_pack::Pack,
    pubkey::Pubkey,
};

use crate::{
    instruction::YesNoInstruction,
    state::{Bet, YesNoQuestion},
};

pub struct Processor {}
impl Processor {
    pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
        let i = YesNoInstruction::unpack(input)?;
        match i {
            YesNoInstruction::CreateQuestion { name, deadline } => {
                msg!("process create question");
                return Self::process_create_question(program_id, accounts, name, deadline);
            }
            YesNoInstruction::Bet { yesno } => {
                msg!("process create question");
                return Self::process_bet(program_id, accounts, yesno);
            }
            YesNoInstruction::CloseQuestion => {
                msg!("process close question");
                return Self::process_close_question(program_id, accounts);
            }
        }
    }

    pub fn process_create_question(
        program_id: &Pubkey,
        accounts: &[AccountInfo],
        title: &str,
        deadline: u64,
    ) -> ProgramResult {
        let accs_iter = &mut accounts.iter();
        let question_account = next_account_info(accs_iter)?;
        let question_issuer = next_account_info(accs_iter)?;
        let spl_mint = next_account_info(accs_iter)?;
        let issuer_spl_account = next_account_info(accs_iter)?;
        let yesno_spl_owner = next_account_info(accs_iter)?;
        let spl_account = next_account_info(accs_iter)?;

        Self::check_owner_and_signer(program_id, vec![question_account], vec![question_issuer])?;

        let mut q = YesNoQuestion::unpack_check_duplicated(&question_account.data.borrow())?;
        let pool: u64 = 100;
        q.setup(title, deadline, pool, question_issuer.key)?;
        q.serialize(&mut &mut question_account.data.borrow_mut()[..])?;

        invoke(
            &spl_token::instruction::mint_to(
                &spl_token::id(),
                spl_mint.key,
                issuer_spl_account.key,
                yesno_spl_owner.key,
                &[yesno_spl_owner.key],
                pool,
            )?,
            &[
                spl_account.clone(),
                spl_mint.clone(),
                issuer_spl_account.clone(),
                yesno_spl_owner.clone(),
            ],
        )?;

        Ok(())
    }

    pub fn process_bet(program_id: &Pubkey, accounts: &[AccountInfo], yesno: u8) -> ProgramResult {
        let accs_iter = &mut accounts.iter();
        let bet_account = next_account_info(accs_iter)?;
        let question_account = next_account_info(accs_iter)?;
        let bet_maker = next_account_info(accs_iter)?;

        Self::check_owner_and_signer(program_id, vec![question_account], vec![bet_maker])?;
        if bet_account.owner != question_account.key {
            return Err(ProgramError::IllegalOwner);
        }

        let mut bet = Bet::unpack_unchecked(&bet_account.data.borrow())?;
        bet.setup(question_account.key, yesno)?;
        let mut question = YesNoQuestion::unpack(&question_account.data.borrow())?;
        question.add(yesno)?;

        bet.serialize(&mut &mut bet_account.data.borrow_mut()[..])?;
        question.serialize(&mut &mut question_account.data.borrow_mut()[..])?;
        Ok(())
    }

    pub fn process_close_question(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
        Ok(())
    }

    fn check_owner_and_signer(
        owner: &Pubkey,
        owner_accounts: Vec<&AccountInfo>,
        signers: Vec<&AccountInfo>,
    ) -> ProgramResult {
        for acc in owner_accounts.iter() {
            if acc.owner != owner {
                return Err(ProgramError::IllegalOwner);
            }
        }

        for acc in signers.iter() {
            if !acc.is_signer {
                return Err(ProgramError::MissingRequiredSignature);
            }
        }

        return Ok(());
    }
}