"""CoinGecko model"""
__docformat__ = "numpy"

# pylint: disable=C0301, E1101

import logging
import re
from typing import Any, List

import numpy as np
import pandas as pd
from pycoingecko import CoinGeckoAPI

from gamestonk_terminal.cryptocurrency.dataframe_helpers import (
    create_df_index,
    long_number_format_with_type_check,
    replace_underscores_in_column_names,
)
from gamestonk_terminal.cryptocurrency.discovery.pycoingecko_model import get_coins
from gamestonk_terminal.decorators import log_start_end

logger = logging.getLogger(__name__)

HOLD_COINS = ["ethereum", "bitcoin"]

NEWS_FILTERS = ["Index", "Title", "Author", "Posted"]

CATEGORIES_FILTERS = [
    "Rank",
    "Name",
    "Change_1h",
    "Change_24h",
    "Change_7d",
    "Market_Cap",
    "Volume_24h",
    "Coins",
]

STABLES_FILTERS = [
    "Rank",
    "Name",
    "Symbol",
    "Price",
    "Change_24h",
    "Exchanges",
    "Market_Cap",
    "Change_30d",
]

PRODUCTS_FILTERS = [
    "Rank",
    "Platform",
    "Identifier",
    "Supply_Rate",
    "Borrow_Rate",
]

PLATFORMS_FILTERS = ["Rank", "Name", "Category", "Centralized"]

EXCHANGES_FILTERS = [
    "Rank",
    "Trust_Score",
    "Id",
    "Name",
    "Country",
    "Year Established",
    "Trade_Volume_24h_BTC",
]

EXRATES_FILTERS = ["Index", "Name", "Unit", "Value", "Type"]

INDEXES_FILTERS = ["Rank", "Name", "Id", "Market", "Last", "MultiAsset"]

DERIVATIVES_FILTERS = [
    "Rank",
    "Market",
    "Symbol",
    "Price",
    "Pct_Change_24h",
    "Contract_Type",
    "Basis",
    "Spread",
    "Funding_Rate",
    "Volume_24h",
]

COINS_COLUMNS = [
    "symbol",
    "name",
    "current_price",
    "market_cap",
    "market_cap_rank",
    "price_change_percentage_7d_in_currency",
    "price_change_percentage_24h_in_currency",
    "total_volume",
]


@log_start_end(log=logger)
def get_holdings_overview(endpoint: str = "bitcoin") -> List[Any]:
    """Returns public companies that holds ethereum or bitcoin [Source: CoinGecko]

    Parameters
    ----------
    endpoint : str
        "bitcoin" or "ethereum"

    Returns
    -------
    List:
        - str:              Overall statistics
        - pandas.DataFrame: Companies holding crypto
    """
    cg = CoinGeckoAPI()
    data = cg.get_companies_public_treasury_by_coin_id(coin_id=endpoint)

    stats_str = f"""{len(data["companies"])} companies hold a total of {long_number_format_with_type_check(data["total_holdings"])} {endpoint} ({data["market_cap_dominance"]}% of market cap dominance) with the current value of {long_number_format_with_type_check(int(data["total_value_usd"]))} USD dollars"""  # noqa

    df = pd.json_normalize(data, record_path=["companies"])

    df.columns = list(
        map(
            lambda x: replace_underscores_in_column_names(x)
            if isinstance(x, str)
            else x,
            df.columns,
        )
    )

    return [stats_str, df]


SORT_VALUES = [
    "market_cap_desc",
    "market_cap_asc",
    "name_desc",
    "name_asc",
    "market_cap_change_24h_desc",
    "market_cap_change_24h_asc",
]


@log_start_end(log=logger)
def coin_formatter(n):
    # TODO: can be improved
    coins = []
    re_str = "small/(.*)(.jpg|.png|.JPG|.PNG)"
    for coin in n:
        if re.search(re_str, coin):
            coin_stripped = re.search(re_str, coin).group(1)
            coins.append(coin_stripped)
    return ",".join(coins)


@log_start_end(log=logger)
def get_top_crypto_categories(sort_filter: str = SORT_VALUES[0]) -> pd.DataFrame:
    """Returns top crypto categories [Source: CoinGecko]

    Returns
    -------
    pandas.DataFrame
       Rank, Name, Change_1h, Change_7d, Market_Cap, Volume_24h,Coins, Url
    """
    if sort_filter in SORT_VALUES:
        client = CoinGeckoAPI()
        data = client.get_coins_categories()
        df = pd.DataFrame(data)
        del df["id"]
        del df["content"]
        del df["updated_at"]
        df["top_3_coins"] = df["top_3_coins"].apply(coin_formatter)
        df.columns = [
            replace_underscores_in_column_names(col) if isinstance(col, str) else col
            for col in df.columns
        ]
        return df
    return pd.DataFrame()


# TODO: add string with overview
@log_start_end(log=logger)
def get_stable_coins(top: int = 20) -> pd.DataFrame:
    """Returns top stable coins [Source: CoinGecko]

    Returns
    -------
    pandas.DataFrame
        Rank, Name, Symbol, Price, Change_24h, Exchanges, Market_Cap, Change_30d, Url
    """

    df = get_coins(top=top, category="stablecoins")
    return df[COINS_COLUMNS]


@log_start_end(log=logger)
def get_exchanges() -> pd.DataFrame:
    """Get list of top exchanges from CoinGecko API [Source: CoinGecko]

    Returns
    -------
    pandas.DataFrame
        Trust_Score, Id, Name, Country, Year_Established, Trade_Volume_24h_BTC, Url
    """

    client = CoinGeckoAPI()
    df = pd.DataFrame(client.get_exchanges_list(per_page=250))
    df.replace({float(np.NaN): None}, inplace=True)
    df = df[
        [
            "trust_score",
            "id",
            "name",
            "country",
            "year_established",
            "trade_volume_24h_btc",
            "url",
        ]
    ]
    df.columns = [
        "Trust_Score",
        "Id",
        "Name",
        "Country",
        "Year_Established",
        "Trade_Volume_24h_BTC",
        "Url",
    ]
    create_df_index(df, "Rank")
    return df


@log_start_end(log=logger)
def get_financial_platforms() -> pd.DataFrame:
    """Get list of financial platforms from CoinGecko API [Source: CoinGecko]

    Returns
    -------
    pandas.DataFrame
        Rank, Name, Category, Centralized, Url
    """

    client = CoinGeckoAPI()
    df = pd.DataFrame(client.get_finance_platforms())
    df.drop("facts", axis=1, inplace=True)
    create_df_index(df, "rank")
    df.columns = ["Rank", "Name", "Category", "Centralized", "Url"]
    return df


@log_start_end(log=logger)
def get_finance_products() -> pd.DataFrame:
    """Get list of financial products from CoinGecko API

    Returns
    -------
    pandas.DataFrame
       Rank,  Platform, Identifier, Supply_Rate, Borrow_Rate
    """

    client = CoinGeckoAPI()
    df = pd.DataFrame(
        client.get_finance_products(per_page=250),
        columns=[
            "platform",
            "identifier",
            "supply_rate_percentage",
            "borrow_rate_percentage",
        ],
    )
    df.columns = ["Platform", "Identifier", "Supply_Rate", "Borrow_Rate"]
    create_df_index(df, "Rank")
    return df


@log_start_end(log=logger)
def get_indexes() -> pd.DataFrame:
    """Get list of crypto indexes from CoinGecko API [Source: CoinGecko]

    Returns
    -------
    pandas.DataFrame
        Name, Id, Market, Last, MultiAsset
    """

    client = CoinGeckoAPI()
    df = pd.DataFrame(client.get_indexes(per_page=250))
    df.columns = ["Name", "Id", "Market", "Last", "MultiAsset"]
    create_df_index(df, "Rank")
    return df


@log_start_end(log=logger)
def get_derivatives() -> pd.DataFrame:
    """Get list of crypto derivatives from CoinGecko API [Source: CoinGecko]

    Returns
    -------
    pandas.DataFrame
        Rank, Market, Symbol, Price, Pct_Change_24h, Contract_Type, Basis, Spread, Funding_Rate, Volume_24h,
    """

    client = CoinGeckoAPI()
    df = pd.DataFrame(client.get_derivatives(include_tickers="unexpired"))
    df.drop(
        ["index", "last_traded_at", "expired_at", "index_id", "open_interest"],
        axis=1,
        inplace=True,
    )

    df.rename(columns={"price_percentage_change_24h": "pct_change_24h"}, inplace=True)
    create_df_index(df, "rank")
    df["price"] = df["price"].apply(
        lambda x: "" if not x else float(x.strip("$").replace(",", ""))
    )

    df.columns = [
        "Rank",
        "Market",
        "Symbol",
        "Price",
        "Pct_Change_24h",
        "Contract_Type",
        "Basis",
        "Spread",
        "Funding_Rate",
        "Volume_24h",
    ]
    return df


@log_start_end(log=logger)
def get_exchange_rates() -> pd.DataFrame:
    """Get list of crypto, fiats, commodity exchange rates from CoinGecko API [Source: CoinGecko]

    Returns
    -------
    pandas.DataFrame
        Index, Name, Unit, Value, Type
    """

    client = CoinGeckoAPI()
    df = pd.DataFrame(client.get_exchange_rates()["rates"]).T.reset_index()
    df.drop("index", axis=1, inplace=True)
    create_df_index(df, "index")
    df.columns = ["Index", "Name", "Unit", "Value", "Type"]
    return df


@log_start_end(log=logger)
def get_global_info() -> pd.DataFrame:
    """Get global statistics about crypto from CoinGecko API like:
        - market cap change
        - number of markets
        - icos
        - number of active crypto

    [Source: CoinGecko]

    Returns
    -------
    pandas.DataFrame
        Metric, Value
    """

    client = CoinGeckoAPI()
    results = client.get_global()

    total_mcap = results.pop("market_cap_percentage")
    btc, eth = total_mcap.get("btc"), total_mcap.get("eth")
    for key in ["total_market_cap", "total_volume", "updated_at"]:
        del results[key]
    results["btc_market_cap_in_pct"] = btc
    results["eth_market_cap_in_pct"] = eth
    results["altcoin_market_cap_in_pct"] = 100 - (float(eth) + float(btc))
    df = pd.Series(results).reset_index()
    df.columns = ["Metric", "Value"]
    df["Metric"] = df["Metric"].apply(
        lambda x: replace_underscores_in_column_names(x) if isinstance(x, str) else x
    )
    return df


@log_start_end(log=logger)
def get_global_markets_info() -> pd.DataFrame:
    """Get global statistics about crypto markets from CoinGecko API like:
        Market_Cap, Volume, Market_Cap_Percentage

    [Source: CoinGecko]

    Returns
    -------
    pandas.DataFrame
        Market_Cap, Volume, Market_Cap_Percentage
    """

    columns = [
        "Market_Cap",
        "Volume",
        "Market_Cap_Percentage",
    ]
    data = []
    client = CoinGeckoAPI()
    results = client.get_global()
    for key in columns:
        data.append(results.get(key))
    df = pd.DataFrame(data).T
    df.columns = columns
    df.replace({float("nan"): None}, inplace=True)
    return df.reset_index()


@log_start_end(log=logger)
def get_global_defi_info() -> pd.DataFrame:
    """Get global statistics about Decentralized Finances [Source: CoinGecko]

    Returns
    -------
    pandas.DataFrame
        Metric, Value
    """

    client = CoinGeckoAPI()
    results = client.get_global_decentralized_finance_defi()
    for key, value in results.items():
        try:
            results[key] = round(float(value), 4)
        except (ValueError, TypeError):
            pass

    df = pd.Series(results).reset_index()
    df.columns = ["Metric", "Value"]
    df["Metric"] = df["Metric"].apply(
        lambda x: replace_underscores_in_column_names(x) if isinstance(x, str) else x
    )
    return df

[((22, 9, 22, 36), 'logging.getLogger', 'logging.getLogger', ({(22, 27, 22, 35): '__name__'}, {}), '(__name__)', False, 'import logging\n'), ((99, 1, 99, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((143, 1, 143, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((155, 1, 155, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((181, 1, 181, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((195, 1, 195, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((232, 1, 232, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((250, 1, 250, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((275, 1, 275, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((292, 1, 292, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((331, 1, 331, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((349, 1, 349, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((383, 1, 383, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((412, 1, 412, 26), 'gamestonk_terminal.decorators.log_start_end', 'log_start_end', (), '', False, 'from gamestonk_terminal.decorators import log_start_end\n'), ((114, 9, 114, 23), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((119, 9, 119, 59), 'pandas.json_normalize', 'pd.json_normalize', (), '', True, 'import pandas as pd\n'), ((177, 11, 177, 25), 'pandas.DataFrame', 'pd.DataFrame', ({}, {}), '()', True, 'import pandas as pd\n'), ((191, 9, 191, 51), 'gamestonk_terminal.cryptocurrency.discovery.pycoingecko_model.get_coins', 'get_coins', (), '', False, 'from gamestonk_terminal.cryptocurrency.discovery.pycoingecko_model import get_coins\n'), ((205, 13, 205, 27), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((228, 4, 228, 31), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.create_df_index', 'create_df_index', ({(228, 20, 228, 22): 'df', (228, 24, 228, 30): '"""Rank"""'}, {}), "(df, 'Rank')", False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((242, 13, 242, 27), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((245, 4, 245, 31), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.create_df_index', 'create_df_index', ({(245, 20, 245, 22): 'df', (245, 24, 245, 30): '"""rank"""'}, {}), "(df, 'rank')", False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((260, 13, 260, 27), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((271, 4, 271, 31), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.create_df_index', 'create_df_index', ({(271, 20, 271, 22): 'df', (271, 24, 271, 30): '"""Rank"""'}, {}), "(df, 'Rank')", False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((285, 13, 285, 27), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((288, 4, 288, 31), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.create_df_index', 'create_df_index', ({(288, 20, 288, 22): 'df', (288, 24, 288, 30): '"""Rank"""'}, {}), "(df, 'Rank')", False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((302, 13, 302, 27), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((311, 4, 311, 31), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.create_df_index', 'create_df_index', ({(311, 20, 311, 22): 'df', (311, 24, 311, 30): '"""rank"""'}, {}), "(df, 'rank')", False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((341, 13, 341, 27), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((344, 4, 344, 32), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.create_df_index', 'create_df_index', ({(344, 20, 344, 22): 'df', (344, 24, 344, 31): '"""index"""'}, {}), "(df, 'index')", False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((365, 13, 365, 27), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((402, 13, 402, 27), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((422, 13, 422, 27), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((149, 11, 149, 34), 're.search', 're.search', ({(149, 21, 149, 27): 're_str', (149, 29, 149, 33): 'coin'}, {}), '(re_str, coin)', False, 'import re\n'), ((165, 17, 165, 31), 'pycoingecko.CoinGeckoAPI', 'CoinGeckoAPI', ({}, {}), '()', False, 'from pycoingecko import CoinGeckoAPI\n'), ((167, 13, 167, 31), 'pandas.DataFrame', 'pd.DataFrame', ({(167, 26, 167, 30): 'data'}, {}), '(data)', True, 'import pandas as pd\n'), ((406, 9, 406, 27), 'pandas.DataFrame', 'pd.DataFrame', ({(406, 22, 406, 26): 'data'}, {}), '(data)', True, 'import pandas as pd\n'), ((117, 72, 117, 130), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.long_number_format_with_type_check', 'long_number_format_with_type_check', ({(117, 107, 117, 129): "data['total_holdings']"}, {}), "(data['total_holdings'])", False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((375, 9, 375, 27), 'pandas.Series', 'pd.Series', ({(375, 19, 375, 26): 'results'}, {}), '(results)', True, 'import pandas as pd\n'), ((430, 9, 430, 27), 'pandas.Series', 'pd.Series', ({(430, 19, 430, 26): 'results'}, {}), '(results)', True, 'import pandas as pd\n'), ((173, 12, 173, 52), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.replace_underscores_in_column_names', 'replace_underscores_in_column_names', ({(173, 48, 173, 51): 'col'}, {}), '(col)', False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((378, 18, 378, 56), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.replace_underscores_in_column_names', 'replace_underscores_in_column_names', ({(378, 54, 378, 55): 'x'}, {}), '(x)', False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((433, 18, 433, 56), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.replace_underscores_in_column_names', 'replace_underscores_in_column_names', ({(433, 54, 433, 55): 'x'}, {}), '(x)', False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((123, 22, 123, 60), 'gamestonk_terminal.cryptocurrency.dataframe_helpers.replace_underscores_in_column_names', 'replace_underscores_in_column_names', ({(123, 58, 123, 59): 'x'}, {}), '(x)', False, 'from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index, long_number_format_with_type_check, replace_underscores_in_column_names\n'), ((150, 28, 150, 51), 're.search', 're.search', ({(150, 38, 150, 44): 're_str', (150, 46, 150, 50): 'coin'}, {}), '(re_str, coin)', False, 'import re\n')]