# -*- 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.async_support.base.exchange import Exchange
from ccxt.abstract.paymium import ImplicitAPI
import hashlib
from ccxt.base.types import Any, Balances, Currency, DepositAddress, Int, Market, Num, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Trade, TransferEntry
from typing import List
from ccxt.base.errors import ExchangeError
from ccxt.base.decimal_to_precision import TICK_SIZE
from ccxt.base.precise import Precise


class paymium(Exchange, ImplicitAPI):

    def describe(self) -> Any:
        return self.deep_extend(super(paymium, self).describe(), {
            'id': 'paymium',
            'name': 'Paymium',
            'countries': ['FR', 'EU'],
            'rateLimit': 2000,
            'version': 'v1',
            'has': {
                'CORS': True,
                'spot': True,
                'margin': None,
                'swap': False,
                'future': False,
                'option': False,
                'cancelOrder': True,
                'createDepositAddress': True,
                'createOrder': True,
                'fetchBalance': True,
                'fetchDepositAddress': True,
                'fetchDepositAddresses': True,
                'fetchDepositAddressesByNetwork': False,
                'fetchFundingHistory': False,
                'fetchFundingRate': False,
                'fetchFundingRateHistory': False,
                'fetchFundingRates': False,
                'fetchIndexOHLCV': False,
                'fetchMarkOHLCV': False,
                'fetchOpenInterestHistory': False,
                'fetchOrderBook': True,
                'fetchPremiumIndexOHLCV': False,
                'fetchTicker': True,
                'fetchTrades': True,
                'fetchTradingFee': False,
                'fetchTradingFees': False,
                'transfer': True,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/87153930-f0f02200-c2c0-11ea-9c0a-40337375ae89.jpg',
                'api': {
                    'rest': 'https://paymium.com/api',
                },
                'www': 'https://www.paymium.com',
                'fees': 'https://www.paymium.com/page/help/fees',
                'doc': [
                    'https://github.com/Paymium/api-documentation',
                    'https://www.paymium.com/page/developers',
                    'https://paymium.github.io/api-documentation/',
                ],
                'referral': 'https://www.paymium.com/page/sign-up?referral=eDAzPoRQFMvaAB8sf-qj',
            },
            'api': {
                'public': {
                    'get': [
                        'countries',
                        'currencies',
                        'data/{currency}/ticker',
                        'data/{currency}/trades',
                        'data/{currency}/depth',
                        'bitcoin_charts/{id}/trades',
                        'bitcoin_charts/{id}/depth',
                    ],
                },
                'private': {
                    'get': [
                        'user',
                        'user/addresses',
                        'user/addresses/{address}',
                        'user/orders',
                        'user/orders/{uuid}',
                        'user/price_alerts',
                        'merchant/get_payment/{uuid}',
                    ],
                    'post': [
                        'user/addresses',
                        'user/orders',
                        'user/withdrawals',
                        'user/email_transfers',
                        'user/payment_requests',
                        'user/price_alerts',
                        'merchant/create_payment',
                    ],
                    'delete': [
                        'user/orders/{uuid}',
                        'user/orders/{uuid}/cancel',
                        'user/price_alerts/{id}',
                    ],
                },
            },
            'markets': {
                'BTC/EUR': self.safe_market_structure({'id': 'eur', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR', 'baseId': 'btc', 'quoteId': 'eur', 'type': 'spot', 'spot': True}),
            },
            'fees': {
                'trading': {
                    'maker': self.parse_number('-0.001'),
                    'taker': self.parse_number('0.005'),
                },
            },
            'precisionMode': TICK_SIZE,
            'features': {
                'spot': {
                    'sandbox': False,
                    'createOrder': {
                        'marginMode': False,
                        'triggerPrice': False,
                        'triggerDirection': False,
                        'triggerPriceType': None,
                        'stopLossPrice': False,
                        'takeProfitPrice': False,
                        'attachedStopLossTakeProfit': None,
                        'timeInForce': {
                            'IOC': False,
                            'FOK': False,
                            'PO': False,
                            'GTD': False,
                        },
                        'hedged': False,
                        'trailing': False,
                        'leverage': False,
                        'marketBuyByCost': True,  # todo
                        'marketBuyRequiresPrice': False,
                        'selfTradePrevention': False,
                        'iceberg': False,
                    },
                    'createOrders': None,
                    'fetchMyTrades': None,
                    'fetchOrder': None,  # todo
                    'fetchOpenOrders': None,  # todo
                    'fetchOrders': None,  # todo
                    'fetchClosedOrders': None,  # todo
                    'fetchOHLCV': None,  # todo
                },
                'swap': {
                    'linear': None,
                    'inverse': None,
                },
                'future': {
                    'linear': None,
                    'inverse': None,
                },
            },
        })

    def parse_balance(self, response) -> Balances:
        result: dict = {'info': response}
        currencies = list(self.currencies.keys())
        for i in range(0, len(currencies)):
            code = currencies[i]
            currency = self.currency(code)
            currencyId = currency['id']
            free = 'balance_' + currencyId
            if free in response:
                account = self.account()
                used = 'locked_' + currencyId
                account['free'] = self.safe_string(response, free)
                account['used'] = self.safe_string(response, used)
                result[code] = account
        return self.safe_balance(result)

    async def fetch_balance(self, params={}) -> Balances:
        """
        query for balance and get the amount of funds available for trading or funds locked in orders

        https://paymium.github.io/api-documentation/#tag/User/paths/~1user/get

        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
        """
        await self.load_markets()
        response = await self.privateGetUser(params)
        return self.parse_balance(response)

    async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
        """
        fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data

        https://paymium.github.io/api-documentation/#tag/Public-data/paths/~1data~1%7Bcurrency%7D~1depth/get

        :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 dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'currency': market['id'],
        }
        response = await self.publicGetDataCurrencyDepth(self.extend(request, params))
        return self.parse_order_book(response, market['symbol'], None, 'bids', 'asks', 'price', 'amount')

    def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
        #
        # {
        #     "high":"33740.82",
        #     "low":"32185.15",
        #     "volume":"4.7890433",
        #     "bid":"33313.53",
        #     "ask":"33497.97",
        #     "midpoint":"33405.75",
        #     "vwap":"32802.5263553",
        #     "at":1643381654,
        #     "price":"33143.91",
        #     "open":"33116.86",
        #     "variation":"0.0817",
        #     "currency":"EUR",
        #     "trade_id":"ce2f5152-3ac5-412d-9b24-9fa72338474c",
        #     "size":"0.00041087"
        # }
        #
        symbol = self.safe_symbol(None, market)
        timestamp = self.safe_timestamp(ticker, 'at')
        vwap = self.safe_string(ticker, 'vwap')
        baseVolume = self.safe_string(ticker, 'volume')
        quoteVolume = Precise.string_mul(baseVolume, vwap)
        last = self.safe_string(ticker, 'price')
        return self.safe_ticker({
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'high': self.safe_string(ticker, 'high'),
            'low': self.safe_string(ticker, 'low'),
            'bid': self.safe_string(ticker, 'bid'),
            'bidVolume': None,
            'ask': self.safe_string(ticker, 'ask'),
            'askVolume': None,
            'vwap': vwap,
            'open': self.safe_string(ticker, 'open'),
            'close': last,
            'last': last,
            'previousClose': None,
            'change': None,
            'percentage': self.safe_string(ticker, 'variation'),
            'average': None,
            'baseVolume': baseVolume,
            'quoteVolume': quoteVolume,
            'info': ticker,
        }, market)

    async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
        """
        fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market

        https://paymium.github.io/api-documentation/#tag/Public-data/paths/~1data~1%7Bcurrency%7D~1ticker/get

        :param str symbol: unified symbol of the market to fetch the ticker for
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'currency': market['id'],
        }
        ticker = await self.publicGetDataCurrencyTicker(self.extend(request, params))
        #
        # {
        #     "high":"33740.82",
        #     "low":"32185.15",
        #     "volume":"4.7890433",
        #     "bid":"33313.53",
        #     "ask":"33497.97",
        #     "midpoint":"33405.75",
        #     "vwap":"32802.5263553",
        #     "at":1643381654,
        #     "price":"33143.91",
        #     "open":"33116.86",
        #     "variation":"0.0817",
        #     "currency":"EUR",
        #     "trade_id":"ce2f5152-3ac5-412d-9b24-9fa72338474c",
        #     "size":"0.00041087"
        # }
        #
        return self.parse_ticker(ticker, market)

    def parse_trade(self, trade: dict, market: Market = None) -> Trade:
        timestamp = self.safe_timestamp(trade, 'created_at_int')
        id = self.safe_string(trade, 'uuid')
        market = self.safe_market(None, market)
        side = self.safe_string(trade, 'side')
        price = self.safe_string(trade, 'price')
        amountField = 'traded_' + market['base'].lower()
        amount = self.safe_string(trade, amountField)
        return self.safe_trade({
            'info': trade,
            'id': id,
            'order': None,
            'timestamp': timestamp,
            'datetime': self.iso8601(timestamp),
            'symbol': market['symbol'],
            'type': None,
            'side': side,
            'takerOrMaker': None,
            'price': price,
            'amount': amount,
            'cost': None,
            'fee': None,
        }, market)

    async def fetch_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://paymium.github.io/api-documentation/#tag/Public-data/paths/~1data~1%7Bcurrency%7D~1trades/get

        :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 Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'currency': market['id'],
        }
        response = await self.publicGetDataCurrencyTrades(self.extend(request, params))
        return self.parse_trades(response, market, since, limit)

    async def create_deposit_address(self, code: str, params={}) -> DepositAddress:
        """
        create a currency deposit address

        https://paymium.github.io/api-documentation/#tag/User/paths/~1user~1addresses/post

        :param str code: unified currency code of the currency for the deposit address
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
        """
        await self.load_markets()
        response = await self.privatePostUserAddresses(params)
        #
        #     {
        #         "address": "1HdjGr6WCTcnmW1tNNsHX7fh4Jr5C2PeKe",
        #         "valid_until": 1620041926,
        #         "currency": "BTC",
        #         "label": "Savings"
        #     }
        #
        return self.parse_deposit_address(response)

    async def fetch_deposit_address(self, code: str, params={}) -> DepositAddress:
        """
        fetch the deposit address for a currency associated with self account

        https://paymium.github.io/api-documentation/#tag/User/paths/~1user~1addresses~1%7Baddress%7D/get

        :param str code: unified currency code
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `address structure <https://docs.ccxt.com/#/?id=address-structure>`
        """
        await self.load_markets()
        request: dict = {
            'address': code,
        }
        response = await self.privateGetUserAddressesAddress(self.extend(request, params))
        #
        #     {
        #         "address": "1HdjGr6WCTcnmW1tNNsHX7fh4Jr5C2PeKe",
        #         "valid_until": 1620041926,
        #         "currency": "BTC",
        #         "label": "Savings"
        #     }
        #
        return self.parse_deposit_address(response)

    async def fetch_deposit_addresses(self, codes: Strings = None, params={}) -> List[DepositAddress]:
        """
        fetch deposit addresses for multiple currencies and chain types

        https://paymium.github.io/api-documentation/#tag/User/paths/~1user~1addresses/get

        :param str[]|None codes: list of unified currency codes, default is None
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a list of `address structures <https://docs.ccxt.com/#/?id=address-structure>`
        """
        await self.load_markets()
        response = await self.privateGetUserAddresses(params)
        #
        #     [
        #         {
        #             "address": "1HdjGr6WCTcnmW1tNNsHX7fh4Jr5C2PeKe",
        #             "valid_until": 1620041926,
        #             "currency": "BTC",
        #             "label": "Savings"
        #         }
        #     ]
        #
        return self.parse_deposit_addresses(response, codes)

    def parse_deposit_address(self, depositAddress, currency: Currency = None) -> DepositAddress:
        #
        #     {
        #         "address": "1HdjGr6WCTcnmW1tNNsHX7fh4Jr5C2PeKe",
        #         "valid_until": 1620041926,
        #         "currency": "BTC",
        #         "label": "Savings"
        #     }
        #
        address = self.safe_string(depositAddress, 'address')
        currencyId = self.safe_string(depositAddress, 'currency')
        return {
            'info': depositAddress,
            'currency': self.safe_currency_code(currencyId, currency),
            'network': None,
            'address': address,
            'tag': None,
        }

    async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
        """
        create a trade order

        https://paymium.github.io/api-documentation/#tag/Order/paths/~1user~1orders/post

        :param str symbol: unified symbol of the market to create an order in
        :param str type: 'market' or 'limit'
        :param str side: 'buy' or 'sell'
        :param float amount: how much of currency you want to trade in units of base currency
        :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        await self.load_markets()
        market = self.market(symbol)
        request: dict = {
            'type': self.capitalize(type) + 'Order',
            'currency': market['id'],
            'direction': side,
            'amount': amount,
        }
        if type != 'market':
            request['price'] = price
        response = await self.privatePostUserOrders(self.extend(request, params))
        return self.safe_order({
            'info': response,
            'id': response['uuid'],
        }, market)

    async def cancel_order(self, id: str, symbol: Str = None, params={}):
        """
        cancels an open order

        https://paymium.github.io/api-documentation/#tag/Order/paths/~1user~1orders~1%7Buuid%7D/delete
        https://paymium.github.io/api-documentation/#tag/Order/paths/~1user~1orders~1%7Buuid%7D~1cancel/delete

        :param str id: order id
        :param str symbol: not used by paymium cancelOrder()
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
        """
        request: dict = {
            'uuid': id,
        }
        response = await self.privateDeleteUserOrdersUuidCancel(self.extend(request, params))
        return self.safe_order({
            'info': response,
        })

    async def transfer(self, code: str, amount: float, fromAccount: str, toAccount: str, params={}) -> TransferEntry:
        """
        transfer currency internally between wallets on the same account

        https://paymium.github.io/api-documentation/#tag/Transfer/paths/~1user~1email_transfers/post

        :param str code: unified currency code
        :param float amount: amount to transfer
        :param str fromAccount: account to transfer from
        :param str toAccount: account to transfer to
        :param dict [params]: extra parameters specific to the exchange API endpoint
        :returns dict: a `transfer structure <https://docs.ccxt.com/#/?id=transfer-structure>`
        """
        await self.load_markets()
        currency = self.currency(code)
        if toAccount.find('@') < 0:
            raise ExchangeError(self.id + ' transfer() only allows transfers to an email address')
        if code != 'BTC' and code != 'EUR':
            raise ExchangeError(self.id + ' transfer() only allows BTC or EUR')
        request: dict = {
            'currency': currency['id'],
            'amount': self.currency_to_precision(code, amount),
            'email': toAccount,
            # 'comment': 'a small note explaining the transfer'
        }
        response = await self.privatePostUserEmailTransfers(self.extend(request, params))
        #
        #     {
        #         "uuid": "968f4580-e26c-4ad8-8bcd-874d23d55296",
        #         "type": "Transfer",
        #         "currency": "BTC",
        #         "currency_amount": "string",
        #         "created_at": "2013-10-24T10:34:37.000Z",
        #         "updated_at": "2013-10-24T10:34:37.000Z",
        #         "amount": "1.0",
        #         "state": "executed",
        #         "currency_fee": "0.0",
        #         "btc_fee": "0.0",
        #         "comment": "string",
        #         "traded_btc": "string",
        #         "traded_currency": "string",
        #         "direction": "buy",
        #         "price": "string",
        #         "account_operations": [
        #             {
        #                 "uuid": "968f4580-e26c-4ad8-8bcd-874d23d55296",
        #                 "amount": "1.0",
        #                 "currency": "BTC",
        #                 "created_at": "2013-10-24T10:34:37.000Z",
        #                 "created_at_int": 1389094259,
        #                 "name": "account_operation",
        #                 "address": "1FPDBXNqSkZMsw1kSkkajcj8berxDQkUoc",
        #                 "tx_hash": "string",
        #                 "is_trading_account": True
        #             }
        #         ]
        #     }
        #
        return self.parse_transfer(response, currency)

    def parse_transfer(self, transfer: dict, currency: Currency = None) -> TransferEntry:
        #
        #     {
        #         "uuid": "968f4580-e26c-4ad8-8bcd-874d23d55296",
        #         "type": "Transfer",
        #         "currency": "BTC",
        #         "currency_amount": "string",
        #         "created_at": "2013-10-24T10:34:37.000Z",
        #         "updated_at": "2013-10-24T10:34:37.000Z",
        #         "amount": "1.0",
        #         "state": "executed",
        #         "currency_fee": "0.0",
        #         "btc_fee": "0.0",
        #         "comment": "string",
        #         "traded_btc": "string",
        #         "traded_currency": "string",
        #         "direction": "buy",
        #         "price": "string",
        #         "account_operations": [
        #             {
        #                 "uuid": "968f4580-e26c-4ad8-8bcd-874d23d55296",
        #                 "amount": "1.0",
        #                 "currency": "BTC",
        #                 "created_at": "2013-10-24T10:34:37.000Z",
        #                 "created_at_int": 1389094259,
        #                 "name": "account_operation",
        #                 "address": "1FPDBXNqSkZMsw1kSkkajcj8berxDQkUoc",
        #                 "tx_hash": "string",
        #                 "is_trading_account": True
        #             }
        #         ]
        #     }
        #
        currencyId = self.safe_string(transfer, 'currency')
        updatedAt = self.safe_string(transfer, 'updated_at')
        timetstamp = self.parse_date(updatedAt)
        accountOperations = self.safe_value(transfer, 'account_operations')
        firstOperation = self.safe_value(accountOperations, 0, {})
        status = self.safe_string(transfer, 'state')
        return {
            'info': transfer,
            'id': self.safe_string(transfer, 'uuid'),
            'timestamp': timetstamp,
            'datetime': self.iso8601(timetstamp),
            'currency': self.safe_currency_code(currencyId, currency),
            'amount': self.safe_number(transfer, 'amount'),
            'fromAccount': None,
            'toAccount': self.safe_string(firstOperation, 'address'),
            'status': self.parse_transfer_status(status),
        }

    def parse_transfer_status(self, status: Str) -> Str:
        statuses: dict = {
            'executed': 'ok',
            # what are the other statuses?
        }
        return self.safe_string(statuses, status, status)

    def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
        url = self.urls['api']['rest'] + '/' + self.version + '/' + self.implode_params(path, params)
        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())
            auth = nonce + url
            headers = {
                'Api-Key': self.apiKey,
                'Api-Nonce': nonce,
            }
            if method == 'POST':
                if query:
                    body = self.json(query)
                    auth += body
                    headers['Content-Type'] = 'application/json'
            else:
                if query:
                    queryString = self.urlencode(query)
                    auth += queryString
                    url += '?' + queryString
            headers['Api-Signature'] = self.hmac(self.encode(auth), self.encode(self.secret), hashlib.sha256)
        return {'url': url, 'method': method, 'body': body, 'headers': headers}

    def handle_errors(self, httpCode: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
        if response is None:
            return None
        errors = self.safe_value(response, 'errors')
        if errors is not None:
            raise ExchangeError(self.id + ' ' + self.json(response))
        return None
