use crate::*;
use std::collections::BTreeMap;
#[wasm_bindgen]
#[derive(Clone, Debug)]
pub struct VotingProposalBuilder {
    proposals: BTreeMap<VotingProposal, Option<ScriptWitnessType>>,
}
#[wasm_bindgen]
impl VotingProposalBuilder {
    pub fn new() -> Self {
        Self {
            proposals: BTreeMap::new(),
        }
    }
    pub fn add(&mut self, proposal: &VotingProposal) -> Result<(), JsError> {
        if proposal.has_script_hash() {
            return Err(JsError::from_str("Proposal has a script hash. Use add_with_plutus_witness instead."));
        }
        self.proposals.insert(proposal.clone(), None);
        Ok(())
    }
    pub fn add_with_plutus_witness(
        &mut self,
        proposal: &VotingProposal,
        witness: &PlutusWitness,
    ) -> Result<(), JsError> {
        self.proposals.insert(
            proposal.clone(),
            Some(ScriptWitnessType::PlutusScriptWitness(witness.clone())),
        );
        Ok(())
    }
    pub fn get_plutus_witnesses(&self) -> PlutusWitnesses {
        let tag = RedeemerTag::new_voting_proposal();
        let mut scripts = PlutusWitnesses::new();
        for (i, (_, script_wit)) in self.proposals.iter().enumerate() {
            if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit {
                let index = BigNum::from(i);
                scripts.add(&s.clone_with_redeemer_index_and_tag(&index, &tag));
            }
        }
        scripts
    }
    pub fn get_ref_inputs(&self) -> TransactionInputs {
        let mut inputs = Vec::new();
        for (_, script_wit) in &self.proposals {
            match script_wit {
                Some(ScriptWitnessType::NativeScriptWitness(script_source)) => {
                    if let NativeScriptSourceEnum::RefInput(input, _, _) = script_source {
                        inputs.push(input.clone());
                    }
                }
                Some(ScriptWitnessType::PlutusScriptWitness(plutus_witness)) => {
                    if let Some(DatumSourceEnum::RefInput(input)) = &plutus_witness.datum {
                        inputs.push(input.clone());
                    }
                    if let PlutusScriptSourceEnum::RefInput(input, _, _) = &plutus_witness.script {
                        inputs.push(input.clone());
                    }
                }
                None => {}
            }
        }
        TransactionInputs(inputs)
    }
    pub(crate) fn get_total_deposit(&self) -> Result<Coin, JsError> {
        self.proposals.iter().fold(
            Ok(Coin::zero()),
            |acc: Result<Coin, JsError>, (proposal, _)| {
                acc.and_then(|acc| {
                    acc.checked_add(&proposal.deposit)
                        .or_else(|_| Err(JsError::from_str("Overflow when calculating total deposit")))
                })
            },
        )
    }
    pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet<Language> {
        let mut used_langs = BTreeSet::new();
        for (_, script_wit) in &self.proposals {
            if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit {
                if let Some(lang) = s.script.language() {
                    used_langs.insert(lang.clone());
                }
            }
        }
        used_langs
    }
    pub fn has_plutus_scripts(&self) -> bool {
        for (_, script_wit) in &self.proposals {
            if let Some(ScriptWitnessType::PlutusScriptWitness(_)) = script_wit {
                return true;
            }
        }
        false
    }
    pub fn build(&self) -> VotingProposals {
        let mut proposals = Vec::new();
        for (voter, _) in &self.proposals {
            proposals.push(voter.clone());
        }
        VotingProposals(proposals)
    }
}