use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use rust_util::XResult;
use crate::tx::Transaction;

#[derive(Debug, Serialize, Deserialize)]
pub struct CreditContract {
    pub name: String,
    pub credit_limit: u32,
    pub issue_amount: u32,
    pub admin: String,
    pub credit: HashMap<String, u32>,
}

impl CreditContract {
    pub fn new(tx: &Transaction, name: &str, credit_limit: u32) -> XResult<Self> {
        let c = Self {
            name: name.into(),
            credit_limit,
            issue_amount: 0,
            admin: tx.sender.clone(),
            credit: HashMap::new(),
        };
        Ok(c)
    }

    pub fn issue(&mut self, tx: &Transaction, receiver: &str, credit: u32) -> XResult<()> {
        if &self.admin != &tx.sender {
            return simple_error!("Current user is not admin, {} vs {:?}", self.admin, tx.sender);
        }
        if credit == 0 {
            return simple_error!("Cannot issue 0 credit!");
        }
        if self.issue_amount + credit > self.credit_limit {
            return simple_error!(
                "Issue too much credit, current: {}, issue: {}, limit: {}, left: {}",
                self.issue_amount,
                credit,
                self.credit_limit,
                self.credit_limit - self.issue_amount
            );
        }
        match self.credit.get_mut(receiver) {
            None => { self.credit.insert(receiver.to_owned(), credit); },
            Some(cr) => *cr += credit,
        }
        self.issue_amount += credit;
        Ok(())
    }

    pub fn transfer(&mut self, tx: &Transaction, receiver: &str, credit: u32) -> XResult<()> {
        if credit == 0 {
            return simple_error!("Cannot transfer 0 credit!");
        }
        match self.credit.get_mut(&tx.sender) {
            None => return simple_error!("Have not enough credit: {:?}", tx.sender),
            Some(cr) => {
                if *cr >= credit {
                    *cr -= credit;
                    match self.credit.get_mut(receiver) {
                        None => { self.credit.insert(receiver.to_owned(), credit); },
                        Some(receiver_credit) => *receiver_credit += credit,
                    }
                } else {
                    return simple_error!("Have not enough credit: {:?}", tx.sender);
                }
            },
        }
        Ok(())
    }

    pub fn query(&self, _tx: &Transaction, account: &str) -> u32 {
        match self.credit.get(account) {
            None => 0,
            Some(receiver_credit) => *receiver_credit,
        }
    }

    pub fn query_all(&self, _tx: &Transaction) -> &HashMap<String, u32> {
        &self.credit
    }
}
