# -*- coding: utf-8 -*-

# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code

import ccxt.async_support
from ccxt.async_support.base.ws.cache import ArrayCache
from ccxt.base.types import Any, IndexType, Int, OrderBook, Trade
from ccxt.async_support.base.ws.client import Client
from typing import List


class luno(ccxt.async_support.luno):

    def describe(self) -> Any:
        return self.deep_extend(super(luno, self).describe(), {
            'has': {
                'ws': True,
                'watchTicker': False,
                'watchTickers': False,
                'watchTrades': True,
                'watchTradesForSymbols': False,
                'watchMyTrades': False,
                'watchOrders': None,  # is in beta
                'watchOrderBook': True,
                'watchOHLCV': False,
            },
            'urls': {
                'api': {
                    'ws': 'wss://ws.luno.com/api/1',
                },
            },
            'options': {
                'sequenceNumbers': {},
            },
            'streaming': {
            },
            'exceptions': {
            },
        })

    async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
        """
        get the list of most recent trades for a particular symbol

        https://www.luno.com/en/developers/api#tag/Streaming-API

        :param str symbol: unified symbol of the market to fetch trades for
        :param int [since]: timestamp in ms of the earliest trade to fetch
        :param int [limit]: the maximum amount of    trades to fetch
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
        """
        self.check_required_credentials()
        await self.load_markets()
        market = self.market(symbol)
        symbol = market['symbol']
        subscriptionHash = '/stream/' + market['id']
        subscription: dict = {'symbol': symbol}
        url = self.urls['api']['ws'] + subscriptionHash
        messageHash = 'trades:' + symbol
        subscribe: dict = {
            'api_key_id': self.apiKey,
            'api_key_secret': self.secret,
        }
        request = self.deep_extend(subscribe, params)
        trades = await self.watch(url, messageHash, request, subscriptionHash, subscription)
        if self.newUpdates:
            limit = trades.getLimit(symbol, limit)
        return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)

    def handle_trades(self, client: Client, message, subscription):
        #
        #     {
        #         "sequence": "110980825",
        #         "trade_updates": [],
        #         "create_update": {
        #             "order_id": "BXHSYXAUMH8C2RW",
        #             "type": "ASK",
        #             "price": "24081.09000000",
        #             "volume": "0.07780000"
        #         },
        #         "delete_update": null,
        #         "status_update": null,
        #         "timestamp": 1660598775360
        #     }
        #
        rawTrades = self.safe_value(message, 'trade_updates', [])
        length = len(rawTrades)
        if length == 0:
            return
        symbol = subscription['symbol']
        market = self.market(symbol)
        messageHash = 'trades:' + symbol
        stored = self.safe_value(self.trades, symbol)
        if stored is None:
            limit = self.safe_integer(self.options, 'tradesLimit', 1000)
            stored = ArrayCache(limit)
            self.trades[symbol] = stored
        for i in range(0, len(rawTrades)):
            rawTrade = rawTrades[i]
            trade = self.parse_trade(rawTrade, market)
            stored.append(trade)
        self.trades[symbol] = stored
        client.resolve(self.trades[symbol], messageHash)

    def parse_trade(self, trade, market=None) -> Trade:
        #
        # watchTrades(public)
        #
        #     {
        #       "base": "69.00000000",
        #       "counter": "113.6499000000000000",
        #       "maker_order_id": "BXEEU4S2BWF5WRB",
        #       "taker_order_id": "BXKNCSF7JDHXY3H",
        #       "order_id": "BXEEU4S2BWF5WRB"
        #     }
        #
        return self.safe_trade({
            'info': trade,
            'id': None,
            'timestamp': None,
            'datetime': None,
            'symbol': market['symbol'],
            'order': None,
            'type': None,
            'side': None,
            # takerOrMaker has no meaning for public trades
            'takerOrMaker': None,
            'price': None,
            'amount': self.safe_string(trade, 'base'),
            'cost': self.safe_string(trade, 'counter'),
            'fee': None,
        }, market)

    async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
        :param str symbol: unified symbol of the market to fetch the order book for
        :param int [limit]: the maximum amount of order book entries to return
        :param dictConstructor [params]: extra parameters specific to the exchange API endpoint
        :param str [params.type]: accepts l2 or l3 for level 2 or level 3 order book
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
        """
        self.check_required_credentials()
        await self.load_markets()
        market = self.market(symbol)
        symbol = market['symbol']
        subscriptionHash = '/stream/' + market['id']
        subscription: dict = {'symbol': symbol}
        url = self.urls['api']['ws'] + subscriptionHash
        messageHash = 'orderbook:' + symbol
        subscribe: dict = {
            'api_key_id': self.apiKey,
            'api_key_secret': self.secret,
        }
        request = self.deep_extend(subscribe, params)
        orderbook = await self.watch(url, messageHash, request, subscriptionHash, subscription)
        return orderbook.limit()

    def handle_order_book(self, client: Client, message, subscription):
        #
        #     {
        #         "sequence": "24352",
        #         "asks": [{
        #             "id": "BXMC2CJ7HNB88U4",
        #             "price": "1234.00",
        #             "volume": "0.93"
        #         }],
        #         "bids": [{
        #             "id": "BXMC2CJ7HNB88U5",
        #             "price": "1201.00",
        #             "volume": "1.22"
        #         }],
        #         "status": "ACTIVE",
        #         "timestamp": 1528884331021
        #     }
        #
        #  update
        #     {
        #         "sequence": "110980825",
        #         "trade_updates": [],
        #         "create_update": {
        #             "order_id": "BXHSYXAUMH8C2RW",
        #             "type": "ASK",
        #             "price": "24081.09000000",
        #             "volume": "0.07780000"
        #         },
        #         "delete_update": null,
        #         "status_update": null,
        #         "timestamp": 1660598775360
        #     }
        #
        symbol = subscription['symbol']
        messageHash = 'orderbook:' + symbol
        timestamp = self.safe_integer(message, 'timestamp')
        if not (symbol in self.orderbooks):
            self.orderbooks[symbol] = self.indexed_order_book({})
        asks = self.safe_value(message, 'asks')
        if asks is not None:
            snapshot = self.custom_parse_order_book(message, symbol, timestamp, 'bids', 'asks', 'price', 'volume', 'id')
            self.orderbooks[symbol] = self.indexed_order_book(snapshot)
        else:
            ob = self.orderbooks[symbol]
            self.handle_delta(ob, message)
            ob['timestamp'] = timestamp
            ob['datetime'] = self.iso8601(timestamp)
        orderbook = self.orderbooks[symbol]
        nonce = self.safe_integer(message, 'sequence')
        orderbook['nonce'] = nonce
        client.resolve(orderbook, messageHash)

    def custom_parse_order_book(self, orderbook, symbol, timestamp=None, bidsKey='bids', asksKey: IndexType = 'asks', priceKey: IndexType = 'price', amountKey: IndexType = 'volume', countOrIdKey: IndexType = 2):
        bids = self.parse_bids_asks(self.safe_value(orderbook, bidsKey, []), priceKey, amountKey, countOrIdKey)
        asks = self.parse_bids_asks(self.safe_value(orderbook, asksKey, []), priceKey, amountKey, countOrIdKey)
        return {
            'symbol': symbol,
            'bids': self.sort_by(bids, 0, True),
            'asks': self.sort_by(asks, 0),
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'nonce': None,
        }

    def parse_bids_asks(self, bidasks, priceKey: IndexType = 'price', amountKey: IndexType = 'volume', thirdKey: IndexType = 2):
        bidasks = self.to_array(bidasks)
        result = []
        for i in range(0, len(bidasks)):
            result.append(self.custom_parse_bid_ask(bidasks[i], priceKey, amountKey, thirdKey))
        return result

    def custom_parse_bid_ask(self, bidask, priceKey: IndexType = 'price', amountKey: IndexType = 'volume', thirdKey: IndexType = 2):
        price = self.safe_number(bidask, priceKey)
        amount = self.safe_number(bidask, amountKey)
        result = [price, amount]
        if thirdKey is not None:
            thirdValue = self.safe_string(bidask, thirdKey)
            result.append(thirdValue)
        return result

    def handle_delta(self, orderbook, message):
        #
        #  create
        #     {
        #         "sequence": "110980825",
        #         "trade_updates": [],
        #         "create_update": {
        #             "order_id": "BXHSYXAUMH8C2RW",
        #             "type": "ASK",
        #             "price": "24081.09000000",
        #             "volume": "0.07780000"
        #         },
        #         "delete_update": null,
        #         "status_update": null,
        #         "timestamp": 1660598775360
        #     }
        #  del         #     {
        #         "sequence": "110980825",
        #         "trade_updates": [],
        #         "create_update": null,
        #         "delete_update": {
        #             "order_id": "BXMC2CJ7HNB88U4"
        #         },
        #         "status_update": null,
        #         "timestamp": 1660598775360
        #     }
        #  trade
        #     {
        #         "sequence": "110980825",
        #         "trade_updates": [
        #             {
        #                 "base": "0.1",
        #                 "counter": "5232.00",
        #                 "maker_order_id": "BXMC2CJ7HNB88U4",
        #                 "taker_order_id": "BXMC2CJ7HNB88U5"
        #             }
        #         ],
        #         "create_update": null,
        #         "delete_update": null,
        #         "status_update": null,
        #         "timestamp": 1660598775360
        #     }
        #
        createUpdate = self.safe_value(message, 'create_update')
        asksOrderSide = orderbook['asks']
        bidsOrderSide = orderbook['bids']
        if createUpdate is not None:
            bidAskArray = self.custom_parse_bid_ask(createUpdate, 'price', 'volume', 'order_id')
            type = self.safe_string(createUpdate, 'type')
            if type == 'ASK':
                asksOrderSide.storeArray(bidAskArray)
            elif type == 'BID':
                bidsOrderSide.storeArray(bidAskArray)
        deleteUpdate = self.safe_value(message, 'delete_update')
        if deleteUpdate is not None:
            orderId = self.safe_string(deleteUpdate, 'order_id')
            asksOrderSide.storeArray([0, 0, orderId])
            bidsOrderSide.storeArray([0, 0, orderId])

    def handle_message(self, client: Client, message):
        if message == '':
            return
        subscriptions = list(client.subscriptions.values())
        handlers = [self.handle_order_book, self.handle_trades]
        for j in range(0, len(handlers)):
            handler = handlers[j]
            handler(client, message, subscriptions[0])
