//! e5: iterate + closure calling &mut self method (using .iter().map())

use fieldwork_borrow::with_;
use std::collections::HashMap;

pub struct FragmentBuilder;
impl FragmentBuilder {
    pub fn some_f(_block0_hash: &usize, _linear_fees: &Block0Configuration) {}
}

pub struct Block0Configuration;

pub struct Wallet {
    confirmed: bool,
}

impl Wallet {
    pub fn confirm_transaction(&mut self) {
        self.confirmed = true;
    }
}

pub struct CommitteeMembersManager;

pub struct VotePlanManager(usize);

pub struct VoteRoundGenerator {
    block0: Block0Configuration,
    block0_hash: usize,
    committee_wallets: HashMap<usize, Wallet>,
    voteplan_managers: HashMap<usize, VotePlanManager>,
    committee_manager: CommitteeMembersManager,
}

impl VoteRoundGenerator {
    // Original problem code (won't compile):
    // pub fn problem(&mut self) {
    //     self.voteplan_managers = self
    //         .voteplan_managers
    //         .iter()
    //         .map(|(id, manager)| {
    //             let manager = Self::some_f2(&self.committee_manager, &self.block0, manager);
    //             self.prepare_tally_fragments(&manager);
    //             (id.clone(), manager)
    //         })
    //         .collect::<HashMap<_, _>>();
    // }

    pub fn problem(&mut self) {
        with_!(self, {
            methods {
                prepare_tally_fragments(mut committee_wallets, block0_hash, block0, _voteplan: &VotePlanManager) {
                    let committee_member = committee_wallets.values_mut().next().unwrap();
                    FragmentBuilder::some_f(block0_hash, block0);
                    committee_member.confirm_transaction();
                }
            }
            fields {
                committee_manager
                mut voteplan_managers
            }
            body {
                *voteplan_managers = voteplan_managers
                    .iter()
                    .map(|(id, manager)| {
                        let manager = Self::some_f2(committee_manager, block0, manager);
                        self.prepare_tally_fragments(&manager);
                        (id.clone(), manager)
                    })
                    .collect::<HashMap<_, _>>();
            }
        });
    }

    fn some_f2(
        _a: &CommitteeMembersManager,
        _b: &Block0Configuration,
        c: &VotePlanManager,
    ) -> VotePlanManager {
        VotePlanManager(c.0 + 1)
    }

    // Original method for reference
    fn _prepare_tally_fragments(&mut self, _voteplan: &VotePlanManager) {
        let committee_member = self.committee_wallets.values_mut().next().unwrap();
        FragmentBuilder::some_f(&self.block0_hash, &self.block0);
        committee_member.confirm_transaction();
    }
}

#[test]
fn test_e5() {
    let mut generator = VoteRoundGenerator {
        block0: Block0Configuration,
        block0_hash: 42,
        committee_wallets: {
            let mut m = HashMap::new();
            m.insert(0, Wallet { confirmed: false });
            m
        },
        voteplan_managers: {
            let mut m = HashMap::new();
            m.insert(1, VotePlanManager(100));
            m.insert(2, VotePlanManager(200));
            m
        },
        committee_manager: CommitteeMembersManager,
    };

    generator.problem();

    // Wallet should be confirmed (prepare_tally_fragments was called)
    assert!(generator.committee_wallets.get(&0).unwrap().confirmed);

    // Managers should be updated
    assert_eq!(generator.voteplan_managers.get(&1).unwrap().0, 101);
    assert_eq!(generator.voteplan_managers.get(&2).unwrap().0, 201);
}
