import os
import sys

root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(root)

# ----------------------------------------------------------------------------

# 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

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

from ccxt.base.precise import Precise  # noqa E402
from ccxt.test.exchange.base import test_shared_methods  # noqa E402

def test_market(exchange, skipped_properties, method, market):
    format = {
        'id': 'btcusd',
        'symbol': 'BTC/USD',
        'base': 'BTC',
        'quote': 'USD',
        'taker': exchange.parse_number('0.0011'),
        'maker': exchange.parse_number('0.0009'),
        'baseId': 'btc',
        'quoteId': 'usd',
        'active': False,
        'type': 'spot',
        'linear': False,
        'inverse': False,
        'spot': False,
        'swap': False,
        'future': False,
        'option': False,
        'margin': False,
        'contract': False,
        'contractSize': exchange.parse_number('0.001'),
        'expiry': 1656057600000,
        'expiryDatetime': '2022-06-24T08:00:00.000Z',
        'optionType': 'put',
        'strike': exchange.parse_number('56000'),
        'settle': 'XYZ',
        'settleId': 'Xyz',
        'precision': {
            'price': exchange.parse_number('0.001'),
            'amount': exchange.parse_number('0.001'),
            'cost': exchange.parse_number('0.001'),
        },
        'limits': {
            'amount': {
                'min': exchange.parse_number('0.01'),
                'max': exchange.parse_number('1000'),
            },
            'price': {
                'min': exchange.parse_number('0.01'),
                'max': exchange.parse_number('1000'),
            },
            'cost': {
                'min': exchange.parse_number('0.01'),
                'max': exchange.parse_number('1000'),
            },
        },
        'marginModes': {
            'cross': True,
            'isolated': False,
        },
        'info': {},
    }
    # temporary: only test QUANTO markets where that prop exists (todo: add in type later)
    if 'quanto' in market:
        format['quanto'] = False  # whether the market is QUANTO or not
    # define locals
    spot = market['spot']
    contract = market['contract']
    swap = market['swap']
    future = market['future']
    option = market['option']
    index = exchange.safe_bool(market, 'index')  # todo: unify
    is_index = (index is not None) and index
    linear = market['linear']
    inverse = market['inverse']
    quanto = exchange.safe_bool(market, 'quanto')  # todo: unify
    is_quanto = (quanto is not None) and quanto
    #
    empty_allowed_for = ['margin']
    if not contract:
        empty_allowed_for.append('contractSize')
        empty_allowed_for.append('linear')
        empty_allowed_for.append('inverse')
        empty_allowed_for.append('quanto')
        empty_allowed_for.append('settle')
        empty_allowed_for.append('settleId')
    if not future and not option:
        empty_allowed_for.append('expiry')
        empty_allowed_for.append('expiryDatetime')
    if not option:
        empty_allowed_for.append('optionType')
        empty_allowed_for.append('strike')
    test_shared_methods.assert_structure(exchange, skipped_properties, method, market, format, empty_allowed_for)
    test_shared_methods.assert_symbol(exchange, skipped_properties, method, market, 'symbol')
    log_text = test_shared_methods.log_template(exchange, method, market)
    # check taker/maker
    # todo: check not all to be within 0-1.0
    test_shared_methods.assert_greater(exchange, skipped_properties, method, market, 'taker', '-100')
    test_shared_methods.assert_less(exchange, skipped_properties, method, market, 'taker', '100')
    test_shared_methods.assert_greater(exchange, skipped_properties, method, market, 'maker', '-100')
    test_shared_methods.assert_less(exchange, skipped_properties, method, market, 'maker', '100')
    # validate type
    valid_types = ['spot', 'margin', 'swap', 'future', 'option', 'index', 'other']
    test_shared_methods.assert_in_array(exchange, skipped_properties, method, market, 'type', valid_types)
    # validate subTypes
    valid_sub_types = ['linear', 'inverse', 'quanto', None]
    test_shared_methods.assert_in_array(exchange, skipped_properties, method, market, 'subType', valid_sub_types)
    # check if 'type' is consistent
    checked_types = ['spot', 'swap', 'future', 'option']
    for i in range(0, len(checked_types)):
        type = checked_types[i]
        if market[type]:
            assert type == market['type'], 'market.type (' + market['type'] + ') not equal to "' + type + '"' + log_text
    # check if 'subType' is consistent
    if swap or future:
        checked_sub_types = ['linear', 'inverse']
        for i in range(0, len(checked_sub_types)):
            sub_type = checked_sub_types[i]
            if market[sub_type]:
                assert sub_type == market['subType'], 'market.subType (' + market['subType'] + ') not equal to "' + sub_type + '"' + log_text
    # margin check (todo: add margin as mandatory, instead of undefined)
    if spot:
        # for spot market, 'margin' can be either true/false or undefined
        test_shared_methods.assert_in_array(exchange, skipped_properties, method, market, 'margin', [True, False, None])
    else:
        # otherwise, it must be false or undefined
        test_shared_methods.assert_in_array(exchange, skipped_properties, method, market, 'margin', [False, None])
    # check mutually exclusive fields
    if spot:
        assert not contract and linear is None and inverse is None and not option and not swap and not future, 'for spot market, none of contract/linear/inverse/option/swap/future should be set' + log_text
    else:
        # if not spot, any of the below should be true
        assert contract and (future or swap or option or is_index), 'for non-spot markets, any of (future/swap/option/index) should be set' + log_text
    contract_size = exchange.safe_string(market, 'contractSize')
    # contract fields
    if contract:
        if is_quanto:
            assert linear is False, 'linear must be false when "quanto" is true' + log_text
            assert inverse is False, 'inverse must be false when "quanto" is true' + log_text
        else:
            # if false or undefined
            assert inverse is not None, 'inverse must be defined when "contract" is true' + log_text
            assert linear is not None, 'linear must be defined when "contract" is true' + log_text
            assert linear != inverse, 'linear and inverse must not be the same' + log_text
        # contract size should be defined
        assert (('contractSize' in skipped_properties) or contract_size is not None), '"contractSize" must be defined when "contract" is true' + log_text
        # contract size should be above zero
        assert ('contractSize' in skipped_properties) or Precise.string_gt(contract_size, '0'), '"contractSize" must be > 0 when "contract" is true' + log_text
        # settle should be defined
        assert ('settle' in skipped_properties) or (market['settle'] is not None and market['settleId'] is not None), '"settle" & "settleId" must be defined when "contract" is true' + log_text
    else:
        # linear & inverse needs to be undefined
        assert linear is None and inverse is None and quanto is None, 'market linear and inverse (and quanto) must be undefined when "contract" is false' + log_text
        # contract size should be undefined
        assert contract_size is None, '"contractSize" must be undefined when "contract" is false' + log_text
        # settle should be undefined
        assert (market['settle'] is None) and (market['settleId'] is None), '"settle" must be undefined when "contract" is false' + log_text
    # future, swap and option should be mutually exclusive
    if market['future']:
        assert not market['swap'] and not market['option'] and not is_index, 'market swap and option must be false when "future" is true' + log_text
    elif market['swap']:
        assert not market['future'] and not market['option'], 'market future and option must be false when "swap" is true' + log_text
    elif market['option']:
        assert not market['future'] and not market['swap'], 'market future and swap must be false when "option" is true' + log_text
    # check specific fields for options & futures
    if option or future:
        # future or option markets need 'expiry' and 'expiryDatetime'
        assert market['expiry'] is not None, '"expiry" must be defined when "future" is true' + log_text
        assert market['expiryDatetime'] is not None, '"expiryDatetime" must be defined when "future" is true' + log_text
        # expiry datetime should be correct
        iso_string = exchange.iso8601(market['expiry'])
        assert market['expiryDatetime'] == iso_string, 'expiryDatetime ("' + market['expiryDatetime'] + '") must be equal to expiry in iso8601 format "' + iso_string + '"' + log_text
        test_shared_methods.assert_greater(exchange, skipped_properties, method, market, 'expiry', '0')
        if option:
            # strike should be defined
            assert (('strike' in skipped_properties) or market['strike'] is not None), '"strike" must be defined when "option" is true' + log_text
            test_shared_methods.assert_greater(exchange, skipped_properties, method, market, 'strike', '0')
            # optionType should be defined
            assert (('optionType' in skipped_properties) or market['optionType'] is not None), '"optionType" must be defined when "option" is true' + log_text
            test_shared_methods.assert_in_array(exchange, skipped_properties, method, market, 'optionType', ['put', 'call'])
        else:
            # if not option, then strike and optionType should be undefined
            assert market['strike'] is None, '"strike" must be undefined when "option" is false' + log_text
            assert market['optionType'] is None, '"optionType" must be undefined when "option" is false' + log_text
    elif spot:
        # otherwise, expiry needs to be undefined
        assert (market['expiry'] is None) and (market['expiryDatetime'] is None), '"expiry" and "expiryDatetime" must be undefined when it is not future|option market' + log_text
    # check precisions
    precision_keys = list(market['precision'].keys())
    precision_keys_len = len(precision_keys)
    assert precision_keys_len >= 2, 'precision should have "amount" and "price" keys at least' + log_text
    for i in range(0, len(precision_keys)):
        price_or_amount_key = precision_keys[i]
        # only allow very high priced markets (wher coin costs around 100k) to have a 5$ price tickSize
        is_exclusive_pair = market['baseId'] == 'BTC'
        is_non_spot = not spot  # such high precision is only allowed in contract markets
        is_price = price_or_amount_key == 'price'
        is_tick_size_5 = Precise.string_eq('5', exchange.safe_string(market['precision'], price_or_amount_key))
        if is_non_spot and is_price and is_exclusive_pair and is_tick_size_5:
            continue
        if not ('precision' in skipped_properties):
            test_shared_methods.check_precision_accuracy(exchange, skipped_properties, method, market['precision'], price_or_amount_key)
    is_inactive_market = market['active'] is False
    # check limits
    limits_keys = list(market['limits'].keys())
    limits_keys_length = len(limits_keys)
    assert limits_keys_length >= 3, 'limits should have "amount", "price" and "cost" keys at least' + log_text
    for i in range(0, len(limits_keys)):
        key = limits_keys[i]
        limit_entry = market['limits'][key]
        if is_inactive_market:
            continue  # check limits
        if not ('limits' in skipped_properties):
            # min >= 0
            test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, limit_entry, 'min', '0')
            # max >= 0
            test_shared_methods.assert_greater(exchange, skipped_properties, method, limit_entry, 'max', '0')
            # max >= min
            min_string = exchange.safe_string(limit_entry, 'min')
            if min_string is not None:
                test_shared_methods.assert_greater_or_equal(exchange, skipped_properties, method, limit_entry, 'max', min_string)
    # check currencies
    test_shared_methods.assert_valid_currency_id_and_code(exchange, skipped_properties, method, market, market['baseId'], market['base'])
    test_shared_methods.assert_valid_currency_id_and_code(exchange, skipped_properties, method, market, market['quoteId'], market['quote'])
    test_shared_methods.assert_valid_currency_id_and_code(exchange, skipped_properties, method, market, market['settleId'], market['settle'])
    # check ts
    test_shared_methods.assert_timestamp(exchange, skipped_properties, method, market, None, 'created')
    # margin modes
    if not ('marginModes' in skipped_properties):
        margin_modes = exchange.safe_dict(market, 'marginModes')  # in future, remove safeDict
        assert 'cross' in margin_modes, 'marginModes should have "cross" key' + log_text
        assert 'isolated' in margin_modes, 'marginModes should have "isolated" key' + log_text
        test_shared_methods.assert_in_array(exchange, skipped_properties, method, margin_modes, 'cross', [True, False, None])
        test_shared_methods.assert_in_array(exchange, skipped_properties, method, margin_modes, 'isolated', [True, False, None])
