"""The Graph model"""
__docformat__ = "numpy"

import datetime
import logging

import pandas as pd
import requests

from openbb_terminal.cryptocurrency.dataframe_helpers import (
    lambda_very_long_number_formatter,
)
from openbb_terminal.decorators import log_start_end
from openbb_terminal.rich_config import console

# pylint: disable=unsupported-assignment-operation

logger = logging.getLogger(__name__)
# pylint: disable=unsupported-assignment-operation

UNI_URL = "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2"

SWAPS_FILTERS = ["Datetime", "USD", "From", "To"]

POOLS_FILTERS = [
    "volumeUSD",
    "token0.name",
    "token0.symbol",
    "token1.name",
    "token1.symbol",
    "volumeUSD",
    "txCount",
]

TOKENS_FILTERS = [
    "index",
    "symbol",
    "name",
    "tradeVolumeUSD",
    "totalLiquidity",
    "txCount",
]

PAIRS_FILTERS = [
    "created",
    "pair",
    "token0",
    "token1",
    "volumeUSD",
    "txCount",
    "totalSupply",
]

# TODO: convert USD values to int. otherwise sort by these columns won't work


@log_start_end(log=logger)
def query_graph(url: str, query: str) -> dict:
    """Helper methods for querying graphql api. [Source: https://thegraph.com/en/]

    Parameters
    ----------
    url: str
        Endpoint url
    query: str
        Graphql query

    Returns
    -------
    dict:
        Dictionary with response data
    """

    response = requests.post(url, json={"query": query})
    if response.status_code == 200:
        return response.json()["data"]
    return {}


@log_start_end(log=logger)
def get_uni_tokens(
    skip: int = 0, limit: int = 100, sortby: str = "index", ascend: bool = False
) -> pd.DataFrame:
    """Get list of tokens trade-able on Uniswap DEX. [Source: https://thegraph.com/en/]

    Parameters
    ----------
    skip: int
        Skip n number of records.
    limit: int
        Show n number of records.
    sortby: str
        The column to sort by
    ascend: bool
        Whether to sort in ascending order

    Returns
    -------
    pd.DataFrame
        Uniswap tokens with trading volume, transaction count, liquidity.
    """

    limit = min(limit, 1000)
    query = f"""
    {{
    tokens(first: {limit}, skip:{skip}) {{
        symbol
        name
        tradeVolumeUSD
        totalLiquidity
        txCount
        }}
    }}
    """

    data = query_graph(UNI_URL, query)
    if not data:
        return pd.DataFrame()
    df = pd.DataFrame(data["tokens"]).reset_index()
    return df.sort_values(by=sortby, ascending=ascend)


@log_start_end(log=logger)
def get_uniswap_stats() -> pd.DataFrame:
    """Get base statistics about Uniswap DEX. [Source: https://thegraph.com/en/]

    uniswapFactory id: 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f - ethereum address on which Uniswap Factory
    smart contract was deployed. The factory contract is deployed once from the off-chain source code, and it contains
    functions that make it possible to create exchange contracts for any ERC20 token that does not already have one.
    It also functions as a registry of ERC20 tokens that have been added to the system, and the exchange with which they
    are associated. More: https://docs.uniswap.org/protocol/V1/guides/connect-to-uniswap
    We use 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f address to fetch all smart contracts that were
    created with usage of this factory.


    Returns
    -------
    pd.DataFrame
        Uniswap DEX statistics like liquidity, volume, number of pairs, number of transactions.
    """

    query = """
    {
    uniswapFactory(id: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"){
        totalVolumeUSD
        totalLiquidityUSD
        pairCount
        txCount
        totalLiquidityUSD
        totalLiquidityETH
    }
    }
    """
    data = query_graph(UNI_URL, query)
    if not data:
        return pd.DataFrame()
    df = pd.Series(data["uniswapFactory"]).reset_index()
    df.columns = ["Metric", "Value"]
    df["Value"] = df["Value"].apply(lambda x: lambda_very_long_number_formatter(x))
    return df


@log_start_end(log=logger)
def get_uniswap_pool_recently_added(
    last_days: int = 14,
    min_volume: int = 100,
    min_liquidity: int = 0,
    min_tx: int = 100,
) -> pd.DataFrame:
    """Get lastly added trade-able pairs on Uniswap with parameters like:
        * number of days the pair has been active,
        * minimum trading volume,
        * minimum liquidity,
        * number of transactions.

    [Source: https://thegraph.com/en/]

    Parameters
    ----------
    last_days: int
        How many days back to look for added pairs.
    min_volume: int
        Minimum volume
    min_liquidity: int
        Minimum liquidity
    min_tx: int
        Minimum number of transactions done in given pool.

    Returns
    -------
    pd.DataFrame
        Lastly added pairs on Uniswap DEX.
    """

    days = int(
        (datetime.datetime.now() - datetime.timedelta(days=last_days)).timestamp()
    )
    query = f"""
    {{
        pairs(first: 1000,
        where: {{createdAtTimestamp_gt: "{days}", volumeUSD_gt: "{min_volume}", reserveUSD_gt: "{min_liquidity}",
        txCount_gt: "{min_tx}" }},
        orderBy: createdAtTimestamp, orderDirection: desc) {{
        token0 {{
            symbol
            name
        }}
        token1 {{
            symbol
            name
        }}
        reserveUSD
        volumeUSD
        createdAtTimestamp
        totalSupply
        txCount
        }}
    }}
    """

    data = query_graph(UNI_URL, query)
    if not data:
        return pd.DataFrame()

    df = pd.json_normalize(data["pairs"])
    df["createdAtTimestamp"] = df["createdAtTimestamp"].apply(
        lambda x: datetime.datetime.fromtimestamp(int(x))
    )

    df["pair"] = df["token0.symbol"] + "/" + df["token1.symbol"]
    df.rename(
        columns={
            "createdAtTimestamp": "created",
            "token0.name": "token0",
            "token1.name": "token1",
        },
        inplace=True,
    )
    return df[
        [
            "created",
            "pair",
            "token0",
            "token1",
            "volumeUSD",
            "txCount",
            "totalSupply",
        ]
    ]


@log_start_end(log=logger)
def get_uni_pools_by_volume() -> pd.DataFrame:
    """Get uniswap pools by volume. [Source: https://thegraph.com/en/]

    Returns
    -------
    pd.DataFrame
        Trade-able pairs listed on Uniswap by top volume.
    """

    query = """
    {
        pairs(first: 1000, where: {reserveUSD_gt: "1000", volumeUSD_gt: "10000"},
        orderBy: volumeUSD, orderDirection: desc) {
        token0 {
            symbol
            name
        }
        token1 {
            symbol
            name
        }
        volumeUSD
        txCount
        }
    }
    """
    data = query_graph(UNI_URL, query)
    if not data:
        return pd.DataFrame()
    df = pd.json_normalize(data["pairs"])
    return df[
        [
            "token0.name",
            "token0.symbol",
            "token1.name",
            "token1.symbol",
            "volumeUSD",
            "txCount",
        ]
    ]


@log_start_end(log=logger)
def get_last_uni_swaps(
    limit: int = 100, sortby: str = "timestamp", ascend: bool = False
) -> pd.DataFrame:
    """Get the last 100 swaps done on Uniswap [Source: https://thegraph.com/en/]

    Parameters
    ----------
    limit: int
        Number of swaps to return. Maximum possible number: 1000.
    sortby: str
        Key by which to sort data. The table can be sorted by every of its columns
        (see https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2).
    ascend: bool
        Flag to sort data descending

    Returns
    -------
    pd.DataFrame
        Last 100 swaps on Uniswap
    """

    limit = min(limit, 1000)

    query = f"""
    {{
        swaps(first: {limit}, orderBy: timestamp, orderDirection: desc) {{
            timestamp
            pair {{
                token0 {{
                    symbol
                }}
                token1 {{
                    symbol
                }}
            }}
            amountUSD
        }}
    }}
    """

    data = query_graph(UNI_URL, query)
    if not data:
        return pd.DataFrame()
    df = pd.json_normalize(data["swaps"])

    df["timestamp"] = df["timestamp"].apply(
        lambda x: datetime.datetime.fromtimestamp(int(x))
    )
    df["amountUSD"] = df["amountUSD"].apply(lambda x: round(float(x), 2))
    to_rename = {
        "timestamp": "Datetime",
        "amountUSD": "USD",
        "pair.token0.symbol": "From",
        "pair.token1.symbol": "To",
    }
    df = df.rename(columns=to_rename)
    if sortby in df.columns:
        df = df.sort_values(by=sortby, ascending=ascend)
    else:
        console.print(f"[red]Dataframe does not have column {sortby}[/red]\n")
    return df
