import datetime

from matchbook.endpoints.baseendpoint import BaseEndpoint
from matchbook import resources
from matchbook.enums import Side, Status, AggregationType
from matchbook.utils import clean_locals


class Betting(BaseEndpoint):

    def get_orders(self, event_ids=None, market_ids=None, runner_ids=None, offer_id=None, offset=0, per_page=500,
                   interval=None, side=Side.Default, status=Status.Default, session=None):
        """
        Get all orders which fit the argument filters.

        :param event_ids: operate only on orders on specified events.
        :type event_ids: comma separated string
        :param market_ids: operate only on orders on specified markets.
        :type market_ids: comma separated string
        :param runner_ids: operate only on orders on specified runners.
        :type runner_ids: comma separated string
        :param offer_id: specific order id to use.
        :param offset: starting point of results. Default 0.
        :type offset: int
        :param per_page: no. of offers returned in a single response, Max 500. Default 20.
        :type per_page: int
        :param interval: check for orders updated/created in last x seconds, status param must be 'matched'.
        :type interval: int
        :param side: filter results by side (dependent on exchange-type). Default None.
        :type side: MatchbookAPI.bin.enums.Side
        :param status: operate only on orders with specified status. Default None.
        :type status: MatchbookAPI.bin.enums.Status
        :param session: requests session to be used.
        :type session: requests.Session
        :returns: Orders data
        :raises: MatchbookAPI.bin.exceptions.ApiError

        """
        params = clean_locals(locals())
        params['exchange-type'] = self.client.exchange_type
        method = 'offers'
        date_time_sent = datetime.datetime.utcnow()
        if offer_id:
            method = 'offers/{0}'.format(offer_id)
            params = {'odds-type': self.client.odds_type}
            response = self.request("GET", self.client.urn_edge, method, params=params, session=session).json()
        else:
            response = self.request(
                "GET", self.client.urn_edge, method, params=params, target='offers', session=session
            )
        date_time_received = datetime.datetime.utcnow()
        return self.process_response(response, resources.Order, date_time_sent, date_time_received)

    def send_orders(self, runner_id, odds, side, stake, temp_id=None, session=None):
        """
        Place an order(s) on a runner, multiple orders can be places by providing lists of the required arguments.

        :param runner_id: runner(s) on which to place bets.
        :type runner_id: int
        :param odds: odds at which we wish to place the bet.
        :type odds: float
        :param side: The type of bet to place, dependent on exchange.
        :type side: MatchbookAPI.bin.enums.Side
        :param stake: amount in account currency to place on the bet.
        :type stake: float
        :param temp_id: A helper ID generated by the client to help understand the correlation between multiple submitted offers and their responses.
        :type temp_id: str
        :param session: requests session to be used.
        :type session: requests.Session
        :returns: Orders responses, i.e. filled or at exchange or errors.
        :raises: MatchbookAPI.bin.exceptions.ApiError

        """
        date_time_sent = datetime.datetime.utcnow()
        params = {
            'offers': [],
            'odds-type': self.client.odds_type,
            'exchange-type': self.client.exchange_type,
            'currency': self.client.currency,
        }
        if isinstance(runner_id, list):
            if isinstance(temp_id, list):
                for i, _ in enumerate(runner_id):
                    params['offers'].append({'runner-id': runner_id[i], 'side': side[i], 'stake': stake[i],
                                             'odds': odds[i], 'temp-id': temp_id[i]})
            else:
                for i, _ in enumerate(runner_id):
                    params['offers'].append({'runner-id': runner_id[i], 'side': side[i], 'stake': stake[i],
                                             'odds': odds[i]})
        else:
            params['offers'].append({'runner-id': runner_id, 'side': side, 'stake': stake, 'odds': odds})
        method = 'offers'
        response = self.request("POST", self.client.urn_edge, method, data=params, session=session)
        date_time_received = datetime.datetime.utcnow()
        return self.process_response(
            response.json().get('offers', []), resources.Order, date_time_sent, date_time_received
        )

    def get_agg_matched_bets(self, event_ids=None, market_ids=None, runner_ids=None, side=None, offset=0, per_page=500,
                             aggregation_type=AggregationType.Default, session=None):
        # TODO: Make aggregate matched bets resource
        """
         Get matched bets aggregated.

        :param event_ids: operate only on orders on specified events.
        :type event_ids: comma separated string
        :param market_ids: operate only on orders on specified markets.
        :type market_ids: comma separated string
        :param runner_ids: operate only on orders on specified runners.
        :type runner_ids: comma separated string
        :param offset: starting point of results. Default 0.
        :type offset: int
        :param per_page: no. of offers returned in a single response, Max 500. Default 20.
        :type per_page: int
        :param side: filter results by side (dependent on exchange-type). Default None.
        :type side: MatchbookAPI.bin.enums.Side
        :param aggregation_type: how to aggregate bets
        :type aggregation_type: matchbook.enums.AggregationType
        :param session: requests session to be used.
        :type session: requests.Session
        :returns: Orders data
        :raises: MatchbookAPI.bin.exceptions.ApiError

         """
        date_time_sent = datetime.datetime.utcnow()
        params = clean_locals(locals())
        method = 'bets/matched/aggregated'
        response = self.request("GET", self.client.urn_edge, method, params=params, target='bets', session=session)
        date_time_received = datetime.datetime.utcnow()
        return self.process_response(
            response, resources.MatchedBets, date_time_sent, date_time_received
        )

    def get_positions(self, event_ids=None, market_ids=None, runner_ids=None, offset=0, per_page=500, session=None):
        #TODO: Make positions resource
        """
        Get potential profit or loss on each runner.
        
        :param event_ids: operate only on orders on specified events.
        :type event_ids: comma separated string
        :param market_ids: operate only on orders on specified markets.
        :type market_ids: comma separated string
        :param runner_ids: operate only on orders on specified runners.
        :type runner_ids: comma separated string
        :param offset: starting point of results. Default 0.
        :type offset: int
        :param per_page: no. of offers returned in a single response, Max 500. Default 20.
        :type per_page: int
        :param session: requests session to be used.
        :type session: requests.Session
        :returns: Orders data
        :raises: MatchbookAPI.bin.exceptions.ApiError
        """
        date_time_sent = datetime.datetime.utcnow()
        params = clean_locals(locals())
        method = 'accounts/positions'
        response = self.request("GET", self.client.urn_edge, method, params=params, session=session)
        date_time_received = datetime.datetime.utcnow()
        return self.process_response(
            response.json().get('bets', []), resources.Order, date_time_sent, date_time_received
        )

    def amend_orders(self, order_id, odds, side, stake, session=None):
        """
        Adjust/Update an order(s), multiple orders can be adjusted by providing lists of the required arguments.

        :param order_id: order id to adjust.
        :type order_id: int
        :param odds: odds at which we wish to place the bet.
        :type odds: float
        :param side: back,lay|win,lose side to place bets on.
        :type side: MatchbookAPI.bin.enums.Side
        :param stake: amount in account currency to place on the bet.
        :type stake: float
        :param session: requests session to be used.
        :type session: requests.Session
        :returns: Orders responses, i.e. filled or at exchange or errors.
        :raises: MatchbookAPI.bin.exceptions.ApiError

        """
        date_time_sent = datetime.datetime.utcnow()
        params = {
            'offers': [],
            'odds-type': self.client.odds_type,
            'exchange-type': self.client.exchange_type,
            'currency': self.client.currency,
        }
        if isinstance(order_id, list):
            method = 'offers'
            for i, _ in enumerate(order_id):
                params['offers'].append({'id': order_id[i], 'side': side[i], 'stake': stake[i], 'odds': odds[i]})
        else:
            method = 'offers/{}'.format(order_id)
            del params['offers']
            params['stake'] = stake
            params['odds'] = odds
        response = self.request('PUT', self.client.urn_edge, method, data=params, session=session)
        date_time_received = datetime.datetime.utcnow()
        return self.process_response(
            response.json().get('offers', response.json()), resources.Order, date_time_sent, date_time_received
        )

    def delete_bulk_orders(self, event_ids=None, market_ids=None, runner_ids=None, offer_ids=None, session=None):
        """
        Delete all orders which fit the argument filters.

        :param event_ids: bulk delete orders on specified events.
        :type event_ids: comma separated string
        :param market_ids: bulk delete orders on specified markets.
        :type market_ids: comma separated string
        :param runner_ids: bulk delete orders on specified runners.
        :type runner_ids: comma separated string
        :param offer_ids: delete specific order id. Max offerids in one delete request is 25
        :type offer_ids: comma separated string
        :param session: requests session to be used.
        :type session: requests.Session
        :returns: orders deletion report.
        :raises: MatchbookAPI.bin.exceptions.ApiError
        """
        params = clean_locals(locals())
        date_time_sent = datetime.datetime.utcnow()
        method = 'offers'
        response = self.request('DELETE', self.client.urn_edge, method, data=params, session=session)
        date_time_received = datetime.datetime.utcnow()
        return self.process_response(
            response.json().get('offers', []), resources.Order, date_time_sent, date_time_received
        )

    def delete_order(self, offer_id, session=None):
        """
        Delete all orders which fit the argument filters.

        :param offer_id: delete specific order id.
        :type offer_id: int
        :param session: requests session to be used.
        :type session: requests.Session
        :returns: order deletion report.
        :raises: MatchbookAPI.bin.exceptions.ApiError

        """
        date_time_sent = datetime.datetime.utcnow()
        method = 'offers/{}'.format(offer_id)
        response = self.request('DELETE', self.client.urn_edge, method, session=session)
        date_time_received = datetime.datetime.utcnow()
        return self.process_response(
            response.json(), resources.Order, date_time_sent, date_time_received
        )
