# -*- 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

from ccxt.base.exchange import Exchange
import math
from ccxt.base.errors import ExchangeError
from ccxt.base.errors import AuthenticationError
from ccxt.base.errors import ArgumentsRequired
from ccxt.base.errors import ExchangeNotAvailable


class cryptocom(Exchange):

    def describe(self):
        return self.deep_extend(super(cryptocom, self).describe(), {
            'id': 'cryptocom',
            'name': 'СryptoСom',
            'countries': ['MT'],
            'rateLimit': 100,
            'version': 'v1',
            'has': {
                'fetchMarkets': True,
                'fetchCurrencies': False,
                'fetchOrderBook': True,
                'fetchOHLCV': 'emulated',
                'fetchTrades': True,
                'fetchTicker': True,
                'fetchTickers': True,
                'fetchBalance': True,
                'createOrder': True,
                'cancelOrder': True,
                'cancelAllOrders': True,
                'fetchOrder': True,
                'fetchOpenOrders': True,
                'fetchClosedOrders': True,
                'fetchMyTrades': True,
            },
            'timeframes': {
                '1m': '1',
                '5m': '5',
                '15m': '15',
                '30m': '30',  # default
                '1h': '60',
                '1d': '1440',
                '1w': '10080',
                '1M': '43200',
            },
            'urls': {
                'logo': 'https://crypto.com/images/crypto_logo_blue-1a354060.png',
                'api': 'https://api.crypto.com',
                'www': 'https://crypto.com',
                'doc': 'https://crypto.com/exchange-doc',
                'fees': 'https://crypto.com/exchange/document/fees-limits',
            },
            'api': {
                'public': {
                    'get': [
                        'symbols',  # List all available market symbols
                        'ticker',  # Get tickers in all available markets
                        'trades',  # Get last 200 trades in a specified market
                        'ticker/price',  # Get latest execution price for all markets
                        'depth',  # Get the order book for a particular market
                    ],
                },
                'private': {
                    'post': [
                        'account',  # List all account balance of user
                        'order',  # Create an order
                        'showOrder',  # Get order detail
                        'orders/cancel',  # Cancel an order
                        'cancelAllOrders',  # Cancel all orders in a particular market
                        'openOrders',  # List all open orders in a particular market
                        'allOrders',  # List all orders in a particular market
                        'myTrades',  # List all executed orders
                    ],
                },
            },
            'fees': {
                'trading': {
                    'percentage': True,
                    'maker': 0.2 / 100,
                    'taker': 0.2 / 100,
                },
            },
            'exceptions': {
                '500': ExchangeError,  # Internal Server Error
                '503': ExchangeNotAvailable,  # Server Busy - The server is temporarily too busy, please retry later
                '100004': AuthenticationError,  # {'code': '100004', 'msg': 'request parameter illegal', 'data': None}
                '100005': AuthenticationError,  # {'code': '100005', 'msg': 'request sign illegal', 'data': None}
            },
            'options': {
                'fetchOrderBook': {
                    'type': 'step0',  # or 'main'
                },
            },
        })

    def fetch_markets(self, params={}):
        #    {
        #       "code":"0",
        #       "msg":"suc",
        #       "data":[
        #          {
        #             "symbol":"crobtc",
        #             "count_coin":"BTC",
        #             "amount_precision":2,
        #             "base_coin":"CRO",
        #             "price_precision":8
        #          }]
        #    }
        response = self.publicGetSymbols(params)
        markets = self.safe_value(response, 'data', {})
        result = []
        for i in range(0, len(markets)):
            market = markets[i]
            id = self.safe_string(market, 'symbol')
            baseId = self.safe_string(market, 'base_coin')
            quoteId = self.safe_string(market, 'count_coin')
            tickerId = baseId.lower() + quoteId.lower()
            base = self.safe_currency_code(baseId)
            quote = self.safe_currency_code(quoteId)
            symbol = base + '/' + quote
            precision = {
                'amount': self.safe_integer(market, 'amount_precision'),
                'price': self.safe_integer(market, 'price_precision'),
            }
            result.append({
                'info': market,
                'id': id,
                'tickerId': tickerId,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'active': True,
                'taker': self.fees['trading']['taker'],
                'maker': self.fees['trading']['maker'],
                'precision': precision,
                'limits': {
                    'amount': {
                        'min': math.pow(10, -precision['amount']),
                        'max': math.pow(10, precision['amount']),
                    },
                    'price': {
                        'min': math.pow(10, -precision['price']),
                        'max': math.pow(10, precision['price']),
                    },
                    'cost': {
                        'min': None,
                        'max': None,
                    },
                },
            })
        return result

    def fetch_order_book(self, symbol, limit=None, params={}):
        self.load_markets()
        request = {
            'symbol': self.market_id(symbol),
        }
        type = None
        if 'type' in params:
            type = params['type']
            params = self.omit(params, 'type')
        else:
            options = self.safe_value(self.options, 'fetchOrderBook', {})
            type = self.safe_string(options, 'type')
        request['type'] = type
        response = self.publicGetDepth(self.extend(request, params))
        #        {
        #        "code":"0",
        #        "msg":"suc",
        #        "data": {
        #            "tick": {
        #                  "asks": [
        #                      [0.00000692, 19623.4180177],
        #                      [0.00000693, 65620.10483685],
        #                      ...
        #                      [0.00062,1000],
        #                  ],
        #                  "bids": [
        #                      [0.0000069, 61216.77],
        #                      [0.00000689, 1947.68],
        #                      ...
        #                      [0.00000347,4079.93]
        #                  ],
        #                  "time": null
        #              }
        #          }
        #        }
        result = self.safe_value(response, 'data', {})
        orderbook = self.safe_value(result, 'tick')
        return self.parse_order_book(orderbook)

    def parse_ticker(self, ticker, market=None):
        timestamp = self.safe_integer(ticker, 'time')
        if timestamp is None:
            timestamp = self.milliseconds()
        symbol = None
        if market is not None:
            symbol = market['symbol']
        open = None
        change = None
        average = None
        last = self.safe_float(ticker, 'last')
        relativeChange = self.safe_float(ticker, 'rose')
        percentage = None
        if (relativeChange is not None) and (relativeChange != -1) and (last is not None):
            open = last / self.sum(1, relativeChange)
            change = last - open
            average = self.sum(last, open) / 2
            percentage = relativeChange * 100
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_float(ticker, 'high'),
            'low': self.safe_float(ticker, 'low'),
            'bid': self.safe_float(ticker, 'buy'),
            'bidVolume': None,
            'ask': self.safe_float(ticker, 'sell'),
            'askVolume': None,
            'vwap': None,
            'open': open,
            'close': last,
            'last': last,
            'previousClose': None,
            'change': change,
            'percentage': percentage,
            'average': average,
            'baseVolume': self.safe_float(ticker, 'vol'),
            'quoteVolume': None,
            'info': ticker,
        }

    def fetch_tickers(self, symbols=None, params={}):
        self.load_markets()
        response = self.publicGetTicker(params)
        #    {
        #      "code":"0",
        #      "msg":"suc",
        #      "data": {
        #        "date": 1588981375165,
        #        "ticker": [
        #          {
        #            "symbol":"crobtc",
        #            "high":"0.00000694",
        #            "vol":"14428176.79384366",
        #            "last":"0.0000069000000000",
        #            "low":"0.00000656",
        #            "buy":"0.00000689",
        #            "sell":"0.00000692",
        #            "change":"0.0454545455",
        #            "rose":"0.0454545455"
        #          },
        #        ]
        #      }
        #    }
        data = self.safe_value(response, 'data', {})
        tickers = self.safe_value(data, 'ticker', [])
        result = {}
        for i in range(0, len(tickers)):
            ticker = tickers[i]
            tickerId = self.safe_string(ticker, 'symbol')
            market = self.find_market_by_ticker_id(tickerId)
            if market is not None:
                result[market['symbol']] = self.parse_ticker(ticker, market)
            else:
                result[tickerId] = self.parse_ticker(ticker)
        return result

    def find_market_by_ticker_id(self, tickerId):
        for i in range(0, len(self.symbols)):
            market = self.markets[self.symbols[i]]
            if market['tickerId'] == tickerId:
                return market

    def fetch_ticker(self, symbol, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = self.publicGetTicker(self.extend(request, params))
        #    {
        #      "code":"0",
        #      "msg":"suc",
        #      "data": {
        #        "high":"0.000007",
        #        "vol":"14781438.38348542",
        #        "last":"0.0000069600000000",
        #        "low":"0.00000674",
        #        "buy":"0.00000696",
        #        "sell":"0.00000699",
        #        "rose":"0.0087082729",
        #        "time":1589017701000
        #      }
        #    }
        data = self.safe_value(response, 'data', {})
        return self.parse_ticker(data, market)

    def parse_trade(self, trade, market=None):
        timestamp = self.safe_integer(trade, 'ctime')
        symbol = None
        if market is not None:
            symbol = market['symbol']
        price = self.safe_float(trade, 'price')
        amount = self.safe_float(trade, 'amount')
        if amount is None:
            amount = self.safe_float(trade, 'volume')
        cost = price * amount
        side = self.safe_string_lower(trade, 'type')
        id = self.safe_string(trade, 'id')
        type = 'limit'  # all trades are still limit trades
        takerOrMaker = None  # can't distinguish my deal from someone else's
        order = None  # can't distinguish my deal from someone else's
        fee = None
        return {
            'info': trade,
            'id': id,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': symbol,
            'side': side,
            'price': price,
            'amount': amount,
            'cost': cost,
            'order': order,
            'takerOrMaker': takerOrMaker,
            'type': type,
            'fee': fee,
        }

    def fetch_trades(self, symbol, since=None, limit=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = self.publicGetTrades(self.extend(request, params))
        #    {
        #      "code": "0",
        #      "msg": "suc",
        #      "data": [
        #        {
        #          "amount": 151.1700000000000000,
        #          "price": 0.0000069600000000,
        #          "ctime": 1589032904000,
        #          "id": 1887449,
        #          "type": "buy"
        #        },
        #      ]
        #    }
        data = self.safe_value(response, 'data', [])
        return self.parse_trades(data, market, since, limit)

    def fetch_balance(self, params={}):
        self.load_markets()
        response = self.privatePostAccount(params)
        #    {
        #      "code": "0",
        #      "msg": "suc",
        #      "data": {
        #        "total_asset": 432323.23,  # user total assets(estimated in BTC)
        #        "coin_list": [
        #          {
        #            "normal": 32323.233,  # usable balance
        #            "locked": 32323.233,  # locked balance, e.g. locked in an active, non-executed order
        #            "btcValuatin": 112.33,  # value equal to BTC
        #            "coin": "btc"  # asset type
        #          },
        #        ]
        #      }
        #    }
        data = self.safe_value(response, 'data', {})
        coin_list = self.safe_value(data, 'coin_list', [])
        result = {}
        for i in range(0, len(coin_list)):
            balance = coin_list[i]
            currencyId = self.safe_string(balance, 'coin')
            code = self.safe_currency_code(currencyId)
            account = self.account()
            #  account['total'] = self.safe_float(balance, 'normal')
            account['free'] = self.safe_float(balance, 'normal')
            account['used'] = self.safe_float(balance, 'locked')
            result[code] = account
        return self.parse_balance(result)

    def create_order(self, symbol, type, side, amount, price=None, params={}):
        self.load_markets()
        market = self.market(symbol)
        orderType = 1 if (type == 'limit') else 2
        request = {
            'symbol': market['id'],
            'volume': self.amount_to_precision(symbol, amount),
            'side': side.upper(),
            'type': orderType,
        }
        if type != 'market':
            request['price'] = self.price_to_precision(symbol, price)
        response = self.privatePostOrder(self.extend(request, params))
        #    {
        #      'code': '0',
        #      'msg': 'suc',
        #      'data': {
        #        'order_id': 67952563
        #      }
        #    }
        data = self.safe_value(response, 'data', {})
        order = self.parse_order(data, market)
        id = data['order_id']
        self.orders[id] = order
        return order

    def cancel_all_orders(self, symbol=None, params={}):
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelAllOrders requires a `symbol` argument')
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        return self.privatePostCancelAllOrders(self.extend(request, params))

    def cancel_order(self, id, symbol=None, params={}):
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' cancelOrder requires a `symbol` argument')
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'order_id': id,
        }
        response = self.privatePostOrdersCancel(self.extend(request, params))
        data = self.safe_value(response, 'data', {})
        return self.parse_order(data)

    def parse_order_status(self, status):
        # 0: INIT, "Initial order"
        # 1: NEW, "New order, Unfinished business enters the market"
        # 2: FILLED, "Full deal"
        # 3: PART_FILLED, "Partial transaction"
        # 4: CANCELED, "Order cancelled"
        # 5: PENDING_CANCEL, "Order will be cancelled"
        # 6. EXPIRED(, "Abnormal order"
        statuses = {
            '0': 'open',
            '1': 'open',
            '2': 'closed',
            '3': 'open',
            '4': 'canceled',
            '5': 'canceled',
            '6': 'expired',
        }
        return self.safe_string(statuses, status, status)

    def parse_order(self, order, market=None):
        created = self.safe_integer(order, 'created_at')
        updated = self.safe_integer(order, 'updated_at')
        symbol = None
        if market is not None:
            symbol = market['symbol']
        amount = self.safe_float(order, 'volume')
        filled = self.safe_float(order, 'deal_volume')
        remaining = self.safe_float(order, 'remain_volume')
        status = self.parse_order_status(self.safe_string(order, 'status'))
        id = self.safe_string(order, 'order_id')
        if id is None:
            id = self.safe_string(order, 'id')
        clientOrderId = id
        price = self.safe_float(order, 'price')
        type = self.safe_string(order, 'type')
        side = self.safe_string_lower(order, 'side')
        trades = self.safe_value(order, 'tradeList')
        return {
            'id': id,
            'clientOrderId': clientOrderId,
            'timestamp': created,
            'datetime': self.iso8601(created),
            'lastTradeTimestamp': updated,
            'status': status,
            'symbol': symbol,
            'type': type,
            'side': side,
            'price': price,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'trades': trades,
            'info': order,
            'cost': None,
            'fee': None,
            'average': None,
        }

    def fetch_order(self, id, symbol=None, params={}):
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOrder requires a `symbol` argument')
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
            'order_id': id,
        }
        response = self.privatePostShowOrder(self.extend(request, params))
        #    {
        #      'code': '0',
        #      'msg': 'suc',
        #      'data': {
        #        'order_info': {
        #          'side': 'SELL',
        #          'total_price': '1.08255100',
        #          'fee': '0.00000000',
        #          'created_at': 1589038246000,
        #          'deal_price': '0.00000000',
        #          'avg_price': '0.00000000',
        #          'countCoin': 'USDT',
        #          'source': 3,
        #          'type': 1,
        #          'side_msg': 'Sell',
        #          'volume': '0.00470000',
        #          'updated_at': 1589038273000,
        #          'price': '230.33000000',
        #          'source_msg': 'API',
        #          'status_msg': 'Canceled',
        #          'deal_volume': '0.00000000',
        #          'fee_coin': 'USDT',
        #          'id': 67991754,
        #          'remain_volume': '0.00470000',
        #          'baseCoin': 'ETH',
        #          'tradeList': [],
        #          'status': 4
        #        }
        #      }
        #    }
        data = self.safe_value(response, 'data', {})
        order_info = self.safe_value(data, 'order_info', [])
        return self.parse_order(order_info, market)

    def fetch_open_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchOpenOrders requires a `symbol` argument')
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = self.privatePostOpenOrders(self.extend(request, params))
        #    {
        #      'code': '0',
        #      'msg': 'suc',
        #      'data': {
        #        'count': 1,
        #        'resultList': [
        #          {
        #            'side': 'SELL',
        #            'total_price': '1.08255100',
        #            'fee': '0.00000000',
        #            'created_at': 1589044302000,
        #            'deal_price': '0.00000000',
        #            'avg_price': '0.00000000',
        #            'countCoin': 'USDT',
        #            'source': 3,
        #            'type': 1,
        #            'side_msg': 'Sell',
        #            'volume': '0.00470000',
        #            'updated_at': 1589044302000,
        #            'price': '230.33000000',
        #            'source_msg': 'API',
        #            'status_msg': 'unsettled',
        #            'deal_volume': '0.00000000',
        #            'fee_coin': 'USDT',
        #            'id': 68087073,
        #            'remain_volume': '0.00470000',
        #            'baseCoin': 'ETH',
        #            'status': 1
        #          }
        #        ]
        #      }
        #    }
        data = self.safe_value(response, 'data', {})
        resultList = self.safe_value(data, 'resultList', [])
        orders = self.parse_orders(resultList, market, since, limit)
        if symbol is not None:
            orders = self.filter_by(orders, 'symbol', symbol)
        return orders

    def fetch_closed_orders(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchClosedOrders requires a `symbol` argument')
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        response = self.privatePostAllOrders(self.extend(request, params))
        #    {
        #      'code': '0',
        #      'msg': 'suc',
        #      'data': {
        #        'count': 1,
        #        'orderList': [
        #          {
        #            'side': 'SELL',
        #            'total_price': '1.08255100',
        #            'fee': '0.00000000',
        #            'created_at': 1589044302000,
        #            'deal_price': '0.00000000',
        #            'avg_price': '0.00000000',
        #            'countCoin': 'USDT',
        #            'source': 3,
        #            'type': 1,
        #            'side_msg': 'Sell',
        #            'volume': '0.00470000',
        #            'updated_at': 1589044302000,
        #            'price': '230.33000000',
        #            'source_msg': 'API',
        #            'status_msg': 'unsettled',
        #            'deal_volume': '0.00000000',
        #            'fee_coin': 'USDT',
        #            'id': 68087073,
        #            'remain_volume': '0.00470000',
        #            'baseCoin': 'ETH',
        #            'status': 1
        #          }
        #        ]
        #      }
        #    }
        data = self.safe_value(response, 'data', {})
        orderList = self.safe_value(data, 'orderList', [])
        orders = self.parse_orders(orderList, market, since, limit)
        if symbol is not None:
            orders = self.filter_by(orders, 'symbol', symbol)
        orders = self.filter_by_array(orders, 'status', ['closed', 'canceled'], False)
        return orders

    def fetch_my_trades(self, symbol=None, since=None, limit=None, params={}):
        self.load_markets()
        if symbol is None:
            raise ArgumentsRequired(self.id + ' fetchMyTrades requires a `symbol` argument')
        market = self.market(symbol)
        request = {
            'symbol': market['id'],
        }
        #    endDate   No   End time, accurate to seconds "yyyy-MM-dd HH:mm:ss"
        #    page      No   Page number
        #    pageSize  No   Page size
        #    startDate No   Start time, accurate to seconds "yyyy-MM-dd HH:mm:ss"
        #    symbol    Yes  Market symbol "btcusdt", See below for details
        response = self.privatePostMyTrades(self.extend(request, params))
        #    {
        #      'code': '0',
        #      'msg': 'suc',
        #      'data': {
        #        'count': 1,
        #        'resultList': [
        #          {
        #            'volume': '0.0047',
        #            'side': 'SELL',
        #            'feeCoin': 'USDT',
        #            'ask_user_id': 71308,
        #            'price': '213.26000000',
        #            'fee': '0.00',
        #            'ctime': 1589046006000,
        #            'deal_price': '1.00',
        #            'id': 1811457,
        #            'type': 'Sell',
        #            'ask_id': 68117598
        #          }
        #        ]
        #      }
        #    }
        data = self.safe_value(response, 'data', {})
        resultList = self.safe_value(data, 'resultList', [])
        return self.parse_trades(resultList, market, since, limit)

    def nonce(self):
        return self.milliseconds()

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api'] + '/' + self.version + '/' + path
        query = self.omit(params, self.extract_params(path))
        if api == 'public':
            if query:
                url += '?' + self.urlencode(query)
        else:
            self.check_required_credentials()
            nonce = str(self.nonce())
            sortedByKey = self.keysort(self.extend({
                'api_key': self.apiKey,
                'time': nonce,
            }, params))
            query = self.urlencode(sortedByKey)
            payload = query.replace('&', '').replace('=', '') + self.secret
            signature = self.hash(self.encode(payload), 'sha256')
            body = self.extend(sortedByKey, {
                'sign': signature,
            })
            body = self.urlencode(body)
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
            }
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, code, reason, url, method, headers, body, response, requestHeaders, requestBody):
        if response is None:
            return
        if code >= 400:
            feedback = self.id + ' ' + body
            if (code == 503) or (code == 500):
                # The server is temporarily too busy, please retry later
                raise ExchangeNotAvailable(feedback)
            # Request rejected due to an input validation error
            if code == 429:
                return
            # {code: 0, msg: "suc", data: ...}
            if body[0] == '{':
                if ('code' in response) and (response['code'] != 0):
                    errorCode = self.safe_string(response, 'code')
                    self.throw_exactly_matched_exception(self.exceptions, errorCode, feedback)
            raise ExchangeError(feedback)
