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

use crate::state::{miner_pda, OreAccountOLD};

use super::OreAccount;

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum VersionedAutomation {
    V2(Automation),
    V1(AutomationOLD),
}

impl VersionedAutomation {
    pub fn amount(&self) -> u64 {
        match self {
            VersionedAutomation::V2(automation) => automation.amount,
            VersionedAutomation::V1(automation) => automation.amount,
        }
    }
    pub fn authority(&self) -> Pubkey {
        match self {
            VersionedAutomation::V2(automation) => automation.authority,
            VersionedAutomation::V1(automation) => automation.authority,
        }
    }
    pub fn balance(&self) -> u64 {
        match self {
            VersionedAutomation::V2(automation) => automation.balance,
            VersionedAutomation::V1(automation) => automation.balance,
        }
    }
    pub fn executor(&self) -> Pubkey {
        match self {
            VersionedAutomation::V2(automation) => automation.executor,
            VersionedAutomation::V1(automation) => automation.executor,
        }
    }
    pub fn fee(&self) -> u64 {
        match self {
            VersionedAutomation::V2(automation) => automation.fee,
            VersionedAutomation::V1(automation) => automation.fee,
        }
    }
    pub fn strategy(&self) -> u64 {
        match self {
            VersionedAutomation::V2(automation) => automation.strategy,
            VersionedAutomation::V1(automation) => automation.strategy,
        }
    }
    pub fn mask(&self) -> u64 {
        match self {
            VersionedAutomation::V2(automation) => automation.mask,
            VersionedAutomation::V1(automation) => automation.mask,
        }
    }
    pub fn reload(&self) -> u64 {
        match self {
            VersionedAutomation::V2(automation) => automation.reload,
            VersionedAutomation::V1(_) => 0,
        }
    }
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
pub struct Automation {
    /// The amount of SOL to deploy on each territory per round.
    pub amount: u64,

    /// The authority of this automation account.
    pub authority: Pubkey,

    /// The amount of SOL this automation has left.
    pub balance: u64,

    /// The executor of this automation account.
    pub executor: Pubkey,

    /// The amount of SOL the executor should receive in fees.
    pub fee: u64,

    /// The strategy this automation uses.
    pub strategy: u64,

    /// The mask of squares this automation should deploy to if preferred strategy.
    /// If strategy is Random, first bit is used to determine how many squares to deploy to.
    pub mask: u64,

    /// Whether or not to auto-reload SOL when the balance is low.
    pub reload: u64,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable, Serialize, Deserialize)]
pub struct AutomationOLD {
    /// The amount of SOL to deploy on each territory per round.
    pub amount: u64,

    /// The authority of this automation account.
    pub authority: Pubkey,

    /// The amount of SOL this automation has left.
    pub balance: u64,

    /// The executor of this automation account.
    pub executor: Pubkey,

    /// The amount of SOL the executor should receive in fees.
    pub fee: u64,

    /// The strategy this automation uses.
    pub strategy: u64,

    /// The mask of squares this automation should deploy to if preferred strategy.
    /// If strategy is Random, first bit is used to determine how many squares to deploy to.
    pub mask: u64,
}

#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)]
pub enum AutomationStrategy {
    Random = 0,
    Preferred = 1,
}

impl AutomationStrategy {
    pub fn from_u64(value: u64) -> Self {
        Self::try_from(value as u8).unwrap()
    }
}

impl Automation {
    pub fn pda(&self) -> (Pubkey, u8) {
        miner_pda(self.authority)
    }
}

account!(OreAccount, Automation);
account!(OreAccountOLD, AutomationOLD);
