from enum import Enum
from datetime import datetime
import uuid
import json
from coinbase_advanced_trader.cb_auth import CBAuth

# Initialize the single instance of CBAuth
cb_auth = CBAuth()


class Side(Enum):
    BUY = 1
    SELL = 0


class Method(Enum):
    POST = "POST"
    GET = "GET"


def generate_client_order_id():
    return str(uuid.uuid4())


def listAccounts(limit=49, cursor=None):
    """
    Get a list of authenticated accounts for the current user.

    This function uses the GET method to retrieve a list of authenticated accounts from the Coinbase Advanced Trade API.

    :param limit: A pagination limit with default of 49 and maximum of 250. If has_next is true, additional orders are available to be fetched with pagination and the cursor value in the response can be passed as cursor parameter in the subsequent request.
    :param cursor: Cursor used for pagination. When provided, the response returns responses after this cursor.
    :return: A dictionary containing the response from the server. A successful response will return a 200 status code. An unexpected error will return a default error response.
    """
    return cb_auth(Method.GET.value, '/api/v3/brokerage/accounts', {'limit': limit, 'cursor': cursor})


def getAccount(account_uuid):
    """
    Get a list of information about an account, given an account UUID.

    This function uses the GET method to retrieve information about an account from the Coinbase Advanced Trade API.

    :param account_uuid: The account's UUID. Use listAccounts() to find account UUIDs.
    :return: A dictionary containing the response from the server. A successful response will return a 200 status code. An unexpected error will return a default error response.
    """
    return cb_auth(Method.GET.value, f'/api/v3/brokerage/accounts/{account_uuid}')


def createOrder(client_order_id, product_id, side, order_type, order_configuration):
    """
    Create an order with the given parameters.

    :param client_order_id: A unique ID generated by the client for this order.
    :param product_id: The ID of the product to order.
    :param side: The side of the order (e.g., 'buy' or 'sell').
    :param order_type: The type of order (e.g., 'limit_limit_gtc').
    :param order_configuration: A dictionary containing order details such as price, size, and post_only.
    :return: A dictionary containing the response from the server.
    """
    payload = {
        "client_order_id": client_order_id,
        "product_id": product_id,
        "side": side,
        "order_configuration": {
            order_type: order_configuration
        }
    }
    # print("Payload being sent to server:", payload)  # For debugging
    return cb_auth(Method.POST.value, '/api/v3/brokerage/orders', payload)


def cancelOrders(order_ids):
    """
    Initiate cancel requests for one or more orders.

    This function uses the POST method to initiate cancel requests for one or more orders on the Coinbase Advanced Trade API.

    :param order_ids: A list of order IDs for which cancel requests should be initiated.
    :return: A dictionary containing the response from the server. A successful response will return a 200 status code. An unexpected error will return a default error response.
    """
    body = json.dumps({"order_ids": order_ids})
    return cb_auth(Method.POST.value, '/api/v3/brokerage/orders/batch_cancel', body)


def listOrders(**kwargs):
    """
    Retrieve a list of historical orders.

    This function uses the GET method to retrieve a list of historical orders from the Coinbase Advanced Trade API.
    The orders are returned in a batch format.

    :param kwargs: Optional parameters that can be passed to the API. These can include:
        'product_id': Optional string of the product ID. Defaults to null, or fetch for all products.
        'order_status': A list of order statuses.
        'limit': A pagination limit with no default set.
        'start_date': Start date to fetch orders from, inclusive.
        'end_date': An optional end date for the query window, exclusive.
        'user_native_currency': (Deprecated) String of the users native currency. Default is `USD`.
        'order_type': Type of orders to return. Default is to return all order types.
        'order_side': Only orders matching this side are returned. Default is to return all sides.
        'cursor': Cursor used for pagination.
        'product_type': Only orders matching this product type are returned. Default is to return all product types.
        'order_placement_source': Only orders matching this placement source are returned. Default is to return RETAIL_ADVANCED placement source.
        'contract_expiry_type': Only orders matching this contract expiry type are returned. Filter is only applied if ProductType is set to FUTURE in the request.
    :return: A dictionary containing the response from the server. This will include details about each order, such as the order ID, product ID, side, type, and status.
    """
    return cb_auth(Method.GET.value, '/api/v3/brokerage/orders/historical/batch', params=kwargs)


def listFills(**kwargs):
    """
    Retrieve a list of fills filtered by optional query parameters.

    This function uses the GET method to retrieve a list of fills from the Coinbase Advanced Trade API.
    The fills are returned in a batch format.

    :param kwargs: Optional parameters that can be passed to the API. These can include:
        'order_id': Optional string of the order ID.
        'product_id': Optional string of the product ID.
        'start_sequence_timestamp': Start date. Only fills with a trade time at or after this start date are returned.
        'end_sequence_timestamp': End date. Only fills with a trade time before this start date are returned.
        'limit': Maximum number of fills to return in response. Defaults to 100.
        'cursor': Cursor used for pagination. When provided, the response returns responses after this cursor.
    :return: A dictionary containing the response from the server. This will include details about each fill, such as the fill ID, product ID, side, type, and status.
    """
    return cb_auth(Method.GET.value, '/api/v3/brokerage/orders/historical/fills', params=kwargs)


def getOrder(order_id):
    """
    Retrieve a single order by order ID.

    This function uses the GET method to retrieve a single order from the Coinbase Advanced Trade API.

    :param order_id: The ID of the order to retrieve.
    :return: A dictionary containing the response from the server. This will include details about the order, such as the order ID, product ID, side, type, and status.
    """
    return cb_auth(Method.GET.value, f'/api/v3/brokerage/orders/historical/{order_id}')


def listProducts(**kwargs):
    """
    Get a list of the available currency pairs for trading.

    This function uses the GET method to retrieve a list of products from the Coinbase Advanced Trade API.

    :param limit: An optional integer describing how many products to return. Default is None.
    :param offset: An optional integer describing the number of products to offset before returning. Default is None.
    :param product_type: An optional string describing the type of products to return. Default is None.
    :param product_ids: An optional list of strings describing the product IDs to return. Default is None.
    :param contract_expiry_type: An optional string describing the contract expiry type. Default is 'UNKNOWN_CONTRACT_EXPIRY_TYPE'.
    :return: A dictionary containing the response from the server. This will include details about each product, such as the product ID, product type, and contract expiry type.
    """
    return cb_auth(Method.GET.value, '/api/v3/brokerage/products', params=kwargs)


def getProduct(product_id):
    """
    Get information on a single product by product ID.

    This function uses the GET method to retrieve information about a single product from the Coinbase Advanced Trade API.

    :param product_id: The ID of the product to retrieve information for.
    :return: A dictionary containing the response from the server. This will include details about the product, such as the product ID, product type, and contract expiry type.
    """
    response = cb_auth(
        Method.GET.value, f'/api/v3/brokerage/products/{product_id}')

    # Check if there's an error in the response
    if 'error' in response and response['error'] == 'PERMISSION_DENIED':
        print(
            f"Error: {response['message']}. Details: {response['error_details']}")
        return None

    return response


def getProductCandles(product_id, start, end, granularity):
    """
    Get rates for a single product by product ID, grouped in buckets.

    This function uses the GET method to retrieve rates for a single product from the Coinbase Advanced Trade API.

    :param product_id: The trading pair.
    :param start: Timestamp for starting range of aggregations, in UNIX time.
    :param end: Timestamp for ending range of aggregations, in UNIX time.
    :param granularity: The time slice value for each candle.
    :return: A dictionary containing the response from the server. This will include details about each candle, such as the open, high, low, close, and volume.
    """
    params = {
        'start': start,
        'end': end,
        'granularity': granularity
    }
    return cb_auth(Method.GET.value, f'/api/v3/brokerage/products/{product_id}/candles', params)


def getMarketTrades(product_id, limit):
    """
    Get snapshot information, by product ID, about the last trades (ticks), best bid/ask, and 24h volume.

    This function uses the GET method to retrieve snapshot information about the last trades from the Coinbase Advanced Trade API.

    :param product_id: The trading pair, i.e., 'BTC-USD'.
    :param limit: Number of trades to return.
    :return: A dictionary containing the response from the server. This will include details about the last trades, such as the best bid/ask, and 24h volume.
    """
    return cb_auth(Method.GET.value, f'/api/v3/brokerage/products/{product_id}/ticker', {'limit': limit})


def getTransactionsSummary(start_date, end_date, user_native_currency='USD', product_type='SPOT', contract_expiry_type='UNKNOWN_CONTRACT_EXPIRY_TYPE'):
    """
    Get a summary of transactions with fee tiers, total volume, and fees.

    This function uses the GET method to retrieve a summary of transactions from the Coinbase Advanced Trade API.

    :param start_date: The start date of the transactions to retrieve, in datetime format.
    :param end_date: The end date of the transactions to retrieve, in datetime format.
    :param user_native_currency: The user's native currency. Default is 'USD'.
    :param product_type: The type of product. Default is 'SPOT'.
    :param contract_expiry_type: Only orders matching this contract expiry type are returned. Only filters response if ProductType is set to 'FUTURE'. Default is 'UNKNOWN_CONTRACT_EXPIRY_TYPE'.
    :return: A dictionary containing the response from the server. This will include details about each transaction, such as fee tiers, total volume, and fees.
    """
    params = {
        'start_date': start_date.strftime('%Y-%m-%dT%H:%M:%SZ'),
        'end_date': end_date.strftime('%Y-%m-%dT%H:%M:%SZ'),
        'user_native_currency': user_native_currency,
        'product_type': product_type,
        'contract_expiry_type': contract_expiry_type
    }
    return cb_auth(Method.GET.value, '/api/v3/brokerage/transaction_summary', params)
