//! Validates accounts structs.

use crate::*;
use vipers::{assert_keys_eq, invariant, unwrap_int, unwrap_opt, Validate};

impl<'info> Validate<'info> for CreateGovernor<'info> {
    fn validate(&self) -> Result<()> {
        invariant!(
            self.smart_wallet.owners.contains(&self.governor.key()),
            GovernorNotFound
        );

        Ok(())
    }
}

impl<'info> Validate<'info> for CreateProposal<'info> {
    fn validate(&self) -> Result<()> {
        Ok(())
    }
}

impl<'info> Validate<'info> for ActivateProposal<'info> {
    fn validate(&self) -> Result<()> {
        assert_keys_eq!(self.governor, self.proposal.governor);
        assert_keys_eq!(self.electorate, self.governor.electorate);
        invariant!(
            self.proposal.get_state()? == ProposalState::Draft,
            ProposalNotDraft
        );

        let earliest_activation_time = unwrap_int!(self
            .governor
            .params
            .voting_delay
            .checked_add(self.proposal.created_at as u64));
        let now = Clock::get()?.unix_timestamp as u64;
        if earliest_activation_time > now {
            msg!(
                "Earliest activation time {}; now: {}",
                earliest_activation_time,
                now
            );
            invariant!(now >= earliest_activation_time, VotingDelayNotMet);
        }

        Ok(())
    }
}

impl<'info> Validate<'info> for CancelProposal<'info> {
    fn validate(&self) -> Result<()> {
        assert_keys_eq!(
            self.proposer,
            self.proposal.proposer,
            "proposer should match recorded"
        );
        assert_keys_eq!(
            self.governor,
            self.proposal.governor,
            "proposal should be under the governor"
        );
        invariant!(
            self.proposal.get_state()? == ProposalState::Draft,
            ProposalNotDraft
        );
        Ok(())
    }
}

impl<'info> Validate<'info> for QueueProposal<'info> {
    fn validate(&self) -> Result<()> {
        assert_keys_eq!(self.governor, self.proposal.governor);
        assert_keys_eq!(self.smart_wallet, self.governor.smart_wallet);
        let now = Clock::get()?.unix_timestamp;
        let proposal_state = unwrap_opt!(self.proposal.state(now), "invalid state");
        if proposal_state != ProposalState::Succeeded {
            msg!(
                "now: {}, voting_ends_at: {}",
                now,
                self.proposal.voting_ends_at
            );
            msg!(
                "for votes: {}, against votes: {}",
                self.proposal.for_votes,
                self.proposal.against_votes,
            );
            msg!(
                "quorum req: {}, abstain votes: {}",
                self.governor.params.quorum_votes,
                self.proposal.abstain_votes,
            );
            invariant!(
                proposal_state == ProposalState::Succeeded,
                "proposal must be succeeded to be queued"
            );
        }
        Ok(())
    }
}

impl<'info> Validate<'info> for NewVote<'info> {
    fn validate(&self) -> Result<()> {
        Ok(())
    }
}

impl<'info> Validate<'info> for SetVote<'info> {
    fn validate(&self) -> Result<()> {
        assert_keys_eq!(self.governor.electorate, self.electorate);
        assert_keys_eq!(
            self.governor,
            self.proposal.governor,
            "proposal should be under the governor"
        );
        assert_keys_eq!(
            self.vote.proposal,
            self.proposal,
            "vote proposal should match"
        );
        invariant!(
            self.proposal.get_state()? == ProposalState::Active,
            ProposalNotActive
        );
        Ok(())
    }
}

impl<'info> Validate<'info> for CreateProposalMeta<'info> {
    fn validate(&self) -> Result<()> {
        assert_keys_eq!(self.proposer, self.proposal.proposer);
        Ok(())
    }
}

impl<'info> Validate<'info> for SetGovernanceParams<'info> {
    fn validate(&self) -> Result<()> {
        assert_keys_eq!(
            self.smart_wallet,
            self.governor.smart_wallet,
            "smart wallet should match"
        );
        Ok(())
    }
}
