library;

use ::errors::PythError;
use ::data_structures::{data_source::*, governance_payload::*, price::*, wormhole_light::{StorageGuardianSet, WormholeVM}};
use std::{bytes::Bytes, hash::Hash};
use std::math::*;
use std::primitive_conversions::{u32::*, u64::*};

pub const MAGIC: u32 = 0x5054474d;

pub struct GovernanceInstruction {
    pub magic: u32,
    pub module: GovernanceModule,
    pub action: GovernanceAction,
    pub target_chain_id: u16,
    pub payload: Bytes,
}

pub enum GovernanceModule {
    Executor: (), // 0
    Target: (), // 1
    EvmExecutor: (), // 2
    StacksTarget: (), // 3
    Invalid: (),
}

pub enum GovernanceAction {
    UpgradeContract: (), // 0
    AuthorizeGovernanceDataSourceTransfer: (), // 1
    SetDataSources: (), // 2
    SetFee: (), // 3
    SetValidPeriod: (), // 4
    RequestGovernanceDataSourceTransfer: (), // 5
    Invalid: (),
}

impl GovernanceInstruction {
    pub fn new(magic: u32,
                module: GovernanceModule,
                action: GovernanceAction,
                target_chain_id: u16,
                payload: Bytes
                ) -> Self {
        Self { magic, module, action, target_chain_id, payload }
    }

    pub fn parse_governance_instruction(encoded_instruction: Bytes) -> Self {
        let mut index = 0;
        let magic = u32::from_be_bytes([
            encoded_instruction.get(index).unwrap(),
            encoded_instruction.get(index + 1).unwrap(),
            encoded_instruction.get(index + 2).unwrap(),
            encoded_instruction.get(index + 3).unwrap(),
        ]);
        require(magic == MAGIC, PythError::InvalidMagic);
        index += 4;

        let mod_number = encoded_instruction.get(index).unwrap();
        let module = match mod_number {
            0 => GovernanceModule::Executor,
            1 => GovernanceModule::Target,
            2 => GovernanceModule::EvmExecutor,
            3 => GovernanceModule::StacksTarget,
            _ => GovernanceModule::Invalid,
        };
        require(match module {
            GovernanceModule::Target => true,
            _ => false,
        }, PythError::InvalidGovernanceTarget);
        index += 1;

        let action_number = encoded_instruction.get(index).unwrap();
        let governance_action = match action_number {
            0 => GovernanceAction::UpgradeContract, // Not implemented
            1 => GovernanceAction::AuthorizeGovernanceDataSourceTransfer,
            2 => GovernanceAction::SetDataSources,
            3 => GovernanceAction::SetFee,
            4 => GovernanceAction::SetValidPeriod,
            5 => GovernanceAction::RequestGovernanceDataSourceTransfer,
            _ => GovernanceAction::Invalid,
        };
        require(match governance_action {
            GovernanceAction::Invalid => false,
            _ => true,
        }, PythError::InvalidGovernanceAction);
        index += 1;

        let target_chain_id = u16::from_be_bytes([
            encoded_instruction.get(index).unwrap(),
            encoded_instruction.get(index + 1).unwrap(),
        ]);
        index += 2;

        let (_, payload) = encoded_instruction.split_at(index);

        GovernanceInstruction::new(
            magic,
            module,
            governance_action,
            target_chain_id,
            payload,
        )
    }

    /// Parse an AuthorizeGovernanceDataSourceTransferPayload (action 2) with minimal validation
    pub fn parse_authorize_governance_data_source_transfer_payload(encoded_payload: Bytes) -> AuthorizeGovernanceDataSourceTransferPayload {
        AuthorizeGovernanceDataSourceTransferPayload {
            claim_vaa: encoded_payload,
        }
    }

    pub fn parse_request_governance_data_source_transfer_payload(encoded_payload: Bytes) -> RequestGovernanceDataSourceTransferPayload {
        let mut index = 0;
        let governance_data_source_index = u32::from_be_bytes([
            encoded_payload.get(index).unwrap(),
            encoded_payload.get(index + 1).unwrap(),
            encoded_payload.get(index + 2).unwrap(),
            encoded_payload.get(index + 3).unwrap(),
        ]);
        index += 4;
        require(index == encoded_payload.len(), PythError::InvalidGovernanceMessage);
        let rdgst = RequestGovernanceDataSourceTransferPayload {
            governance_data_source_index,
        };
        rdgst
    }

    pub fn parse_set_data_sources_payload(encoded_payload: Bytes) -> SetDataSourcesPayload {
        let mut index = 0;
        let data_sources_length = encoded_payload.get(index).unwrap().as_u64();
        index += 1;
        let mut data_sources = Vec::with_capacity(data_sources_length);

        let mut i = 0;
        while i < data_sources_length {
            let (_, slice) = encoded_payload.split_at(index);
            let (slice, _) = slice.split_at(2);
            let chain_id = u16::from_be_bytes([slice.get(0).unwrap(), slice.get(1).unwrap()]);
            index += 2;
            let (_, slice) = encoded_payload.split_at(index);
            let (slice, _) = slice.split_at(32);
            let emitter_address: b256 = slice.into();
            index += 32;

            data_sources.push(DataSource {
                chain_id,
                emitter_address,
            });
            i += 1
        }

        require(index == encoded_payload.len(), PythError::InvalidGovernanceMessage);
        let sds = SetDataSourcesPayload { data_sources };
        sds
    }

    pub fn parse_set_fee_payload(encoded_payload: Bytes) -> SetFeePayload {
        let mut index = 0;
        let val = u64::from_be_bytes([
            encoded_payload.get(index).unwrap(),
            encoded_payload.get(index + 1).unwrap(),
            encoded_payload.get(index + 2).unwrap(),
            encoded_payload.get(index + 3).unwrap(),
            encoded_payload.get(index + 4).unwrap(),
            encoded_payload.get(index + 5).unwrap(),
            encoded_payload.get(index + 6).unwrap(),
            encoded_payload.get(index + 7).unwrap(),
        ]);
        index += 8;
        let expo = u64::from_be_bytes([
            encoded_payload.get(index).unwrap(),
            encoded_payload.get(index + 1).unwrap(),
            encoded_payload.get(index + 2).unwrap(),
            encoded_payload.get(index + 3).unwrap(),
            encoded_payload.get(index + 4).unwrap(),
            encoded_payload.get(index + 5).unwrap(),
            encoded_payload.get(index + 6).unwrap(),
            encoded_payload.get(index + 7).unwrap(),
        ]);
        index += 8;
        require(encoded_payload.len() == index, PythError::InvalidGovernanceMessage);
        let sf = SetFeePayload {
            new_fee: val * 10u64.pow(expo.try_as_u32().unwrap()),
        };
        sf
    }

    pub fn parse_set_valid_period_payload(encoded_payload: Bytes) -> SetValidPeriodPayload {
        let mut index = 0;
        let valid_time_period_seconds = u64::from_be_bytes([
            encoded_payload.get(index).unwrap(),
            encoded_payload.get(index + 1).unwrap(),
            encoded_payload.get(index + 2).unwrap(),
            encoded_payload.get(index + 3).unwrap(),
            encoded_payload.get(index + 4).unwrap(),
            encoded_payload.get(index + 5).unwrap(),
            encoded_payload.get(index + 6).unwrap(),
            encoded_payload.get(index + 7).unwrap(),
        ]);
        index += 8;
        require(index == encoded_payload.len(), PythError::InvalidGovernanceMessage);
        let svp = SetValidPeriodPayload {
            new_valid_period: valid_time_period_seconds,
        };
        svp
    }
}
