"""Momentum Technical Analysis"""
__docformat__ = "numpy"

import logging
from typing import Tuple

import numpy as np
import pandas as pd
import pandas_ta as ta
from sklearn.linear_model import LinearRegression

from openbb_terminal.decorators import log_start_end
from openbb_terminal.common.technical_analysis import ta_helpers
from openbb_terminal.rich_config import console

logger = logging.getLogger(__name__)


@log_start_end(log=logger)
def cci(
    data: pd.DataFrame,
    window: int = 14,
    scalar: float = 0.0015,
) -> pd.DataFrame:
    """Commodity channel index

    Parameters
    ----------
    high_vals: pd.Series
        High values
    low_values: pd.Series
        Low values
    close-values: pd.Series
        Close values
    window: int
        Length of window
    scalar: float
        Scalar variable

    Returns
    -------
    pd.DataFrame
        Dataframe of technical indicator
    """

    close_col = ta_helpers.check_columns(data)
    if close_col is None:
        return pd.DataFrame()
    return pd.DataFrame(
        ta.cci(
            high=data["High"],
            low=data["Low"],
            close=data[close_col],
            length=window,
            scalar=scalar,
        ).dropna()
    )


@log_start_end(log=logger)
def macd(
    data: pd.Series,
    n_fast: int = 12,
    n_slow: int = 26,
    n_signal: int = 9,
) -> pd.DataFrame:
    """Moving average convergence divergence

    Parameters
    ----------
    data: pd.Series
        Values for calculation
    n_fast : int
        Fast period
    n_slow : int
        Slow period
    n_signal : int
        Signal period
    Returns
    -------
    pd.DataFrame
        Dataframe of technical indicator
    """
    if isinstance(data, pd.DataFrame):
        console.print("[red]Please send a series and not a DataFrame.[/red]\n")
        return pd.DataFrame()
    return pd.DataFrame(
        ta.macd(data, fast=n_fast, slow=n_slow, signal=n_signal).dropna()
    )


@log_start_end(log=logger)
def rsi(
    data: pd.Series, window: int = 14, scalar: float = 100, drift: int = 1
) -> pd.DataFrame:
    """Relative strength index

    Parameters
    ----------
    data: pd.Series
        Dataframe of prices
    window: int
        Length of window
    scalar: float
        Scalar variable
    drift: int
        Drift variable

    Returns
    -------
    pd.DataFrame
        Dataframe of technical indicator
    """
    if isinstance(data, pd.DataFrame):
        console.print("[red]Please send a series and not a DataFrame.[/red]\n")
        return pd.DataFrame()
    raw_data = ta.rsi(data, length=window, scalar=scalar, drift=drift)
    if raw_data is None:
        return pd.DataFrame()
    if raw_data.empty:
        return pd.DataFrame()
    return pd.DataFrame(raw_data.dropna())


@log_start_end(log=logger)
def stoch(
    data: pd.DataFrame,
    fastkperiod: int = 14,
    slowdperiod: int = 3,
    slowkperiod: int = 3,
):
    """Stochastic oscillator

    Parameters
    ----------
    data : pd.DataFrame
        Dataframe of OHLC prices
    fastkperiod : int
        Fast k period
    slowdperiod : int
        Slow d period
    slowkperiod : int
        Slow k period
    Returns
    -------
    pd.DataFrame
        Dataframe of technical indicator
    """
    close_col = ta_helpers.check_columns(data)
    if close_col is None:
        return pd.DataFrame()
    return pd.DataFrame(
        ta.stoch(
            high=data["High"],
            low=data["Low"],
            close=data[close_col],
            k=fastkperiod,
            d=slowdperiod,
            smooth_k=slowkperiod,
        ).dropna()
    )


@log_start_end(log=logger)
def fisher(data: pd.DataFrame, window: int = 14) -> pd.DataFrame:
    """Fisher Transform

    Parameters
    ----------
    data : pd.DataFrame
        Dataframe of OHLC prices
    window: int
        Length for indicator window
    Returns
    -------
    df_ta: pd.DataFrame
        Dataframe of technical indicator
    """
    # Daily
    close_col = ta_helpers.check_columns(data, close=False)
    if close_col is None:
        return pd.DataFrame()
    return pd.DataFrame(
        ta.fisher(high=data["High"], low=data["Low"], length=window).dropna()
    )


@log_start_end(log=logger)
def cg(values: pd.Series, window: int) -> pd.DataFrame:
    """Center of gravity

    Parameters
    ----------
    values: pd.DataFrame
        Data to use with close being titled values
    window: int
        Length for indicator window
    Returns
    -------
    pd.DataFrame
        Dataframe of technical indicator
    """
    return pd.DataFrame(ta.cg(close=values, length=window).dropna())


@log_start_end(log=logger)
def clenow_momentum(
    values: pd.Series, window: int = 90
) -> Tuple[float, float, pd.Series]:
    """Gets the Clenow Volatility Adjusted Momentum.  this is defined as the regression coefficient on log prices
    multiplied by the R^2 value of the regression

    Parameters
    ----------
    values: pd.Series
        Values to perform regression for
    window: int
        Length of lookback period

    Returns
    -------
    float:
        R2 of fit to log data
    float:
        Coefficient of linear regression
    pd.Series:
        Values for best fit line

    Examples
    --------
    >>> from openbb_terminal.sdk import openbb
    >>> df = openbb.stocks.load("AAPL")
    >>> openbb.ta.clenow(df["Close"])
    """
    if len(values) < window:
        console.print(
            f"[red]Calculation asks for at least last {window} days of data[/red]"
        )
        return np.nan, np.nan, pd.Series()

    values = values[-window:]

    y = np.log(values)
    X = np.arange(len(y)).reshape(-1, 1)

    lr = LinearRegression()
    lr.fit(X, y)

    r2 = lr.score(X, y)
    coef = lr.coef_[0]
    annualized_coef = (np.exp(coef) ** 252) - 1

    return r2, annualized_coef, pd.Series(lr.predict(X))


@log_start_end(log=logger)
def demark_seq(values: pd.Series) -> pd.DataFrame:
    """Get the integer value for demark sequential indicator

    Parameters
    ----------
    values: pd.Series
        Series of close values

    Returns
    -------
    pd.DataFrame
        Dataframe of UP and DOWN sequential indicators

    Examples
    --------
    >>> from openbb_terminal.sdk import openbb
    >>> df = openbb.stocks.load("AAPL")
    >>> openbb.ta.demark(df["Close"])
    """
    return ta.td_seq(values, asint=True)
