from .base_agent import BaseAgent
from ..exchange import Exchange
from lightmatchingengine.lightmatchingengine import Order,Side
from abc import ABC
from decimal import *
class Base_Distribution_Agent(BaseAgent,ABC):

    def __init__(self,simulation,business,resource,exchanges: list) -> None:
        """
        Agent for distribute of resources.
        business: Business to distribute resources for.
        resource: Resource to distribute.
        exchanges: list of exchanges to distribute resources to.
        """
        self.business=business
        self.resource=resource
        self.exchanges=exchanges
        self.orders={i: {} for i in range(len(self.exchanges))}
        self.target=0
        self.min_price=-1
        self.max_batch_qty=10
        self.tincome=0
        self.tqty=0
        self.income_offset=0
        self.qty_offset=0
        self.trade_curser=0
        super().__init__(simulation)
    
    def set_target(self,target: int):
        """
        Sets the amount of resources the agent should keep in business inventory
        """
        if target<0:
            target=0
        self.target=target


    def set_price_min(self,price: int):
        """
        Sets the min price the agent should distribute the resources for.
        Set to -1 to disable minimum.
        """
        self.min_price=price

    def target_error(self):
        """
        Returns the difference between target and current business inventory.
        If err >0 the business is in surplus
        If err < 0 then agent needs to distribute resources 
        """
        err=self.business.inventory[self.resource]-self.target
        return int(err)
    

    def distribute_resource(self,price_per,amount,cx_id):
        """
        distribute resource from selected exchange.
        Return true if order has been placed.
        """
        # Get exchange
        cx=self.exchanges[cx_id]
       

        if not self.business.inventory[self.resource]>=amount:
            return False # we dont have enough balance
        
        self.business.inventory[self.resource]-=amount

        cx.add_to_account(self.id,self.resource,amount) # prepaid charge account for cx
        order=cx.submit_order(self.id,self.resource,amount,price_per,Side.SELL)
        if order==None: # Order failed
            return False
        self.orders[cx_id][order.order_id]=order
        return order
    
    def collect_balance_from_cxs(self):
        """
        Collects resouces from account inventory in cxs.
        """
        for cx in self.exchanges:
            amount=cx.get_account_resource_amount(self.id,"balance")
            cx.remove_from_account(self.id,"balance",amount)
            self.business.balance+=amount
            
    
    def collect_resource_from_cxs(self,resource):
        """
        Collects resouces from account inventory in cxs.
        """
        for cx in self.exchanges:
            amount=cx.get_account_resource_amount(self.id,resource)
            cx.remove_from_account(self.id,resource,amount)
            self.business.inventory[resource]+=amount

    def update_trades(self) -> list:
        """
        Returns a list of all trades performed by this agent
        """
        trades=[]
        self.tqty=0
        self.tincome=0
        for cx_id in range(len(self.exchanges)):
            cx=self.exchanges[cx_id]
            orders=self.orders[cx_id]
            if len(orders)>0:
                for k,o in orders.items():
                    if k in cx.order_trades_map:
                        trades.extend(cx.order_trades_map[k])
        self.trades=trades
        for t in trades:
            self.tqty+=t.trade_qty
            self.tincome+=t.trade_qty*t.trade_price
        self.tincome=round(self.tincome,2)
        return trades
    @property
    def qty(self):
        return self.tqty+self.qty_offset
    @property
    def income(self):
        return self.tincome+self.income_offset
    
    def confirm_distribution(self,dis_qty,step):
        income_per=self.income/self.qty
        income_to_confirm=income_per*dis_qty
        income_to_confirm=round(income_to_confirm,2)
        self.income_offset-=income_to_confirm
        self.qty_offset-=dis_qty
        self.income_offset=round(self.income_offset,2)
        self.trade_curser=len(self.trades)-1
    
    def reset(self, episode):
        self.qty_offset=0
        self.tqty=0
        self.income_offset=0
        self.tincome=0
        self.trade_curser=0
        self.trades=[]
        return super().reset(episode)