"""Factors model"""
__docformat__ = "numpy"

import logging
from io import BytesIO
from typing import Tuple
from urllib.request import urlopen
from zipfile import ZipFile

import pandas as pd
import statsmodels.api as sm
import yfinance as yf

from openbb_terminal.decorators import log_start_end

logger = logging.getLogger(__name__)


@log_start_end(log=logger)
def get_fama_raw() -> pd.DataFrame:
    """Gets base Fama French data to calculate risk

    Returns
    -------
    fama : pd.DataFrame
        A data with fama french model information
    """
    with urlopen(
        "http://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/F-F_Research_Data_Factors_CSV.zip"
    ) as url:

        # Download Zipfile and create pandas DataFrame
        with ZipFile(BytesIO(url.read())) as zipfile:
            with zipfile.open("F-F_Research_Data_Factors.CSV") as zip_open:
                df = pd.read_csv(
                    zip_open,
                    header=0,
                    names=["Date", "MKT-RF", "SMB", "HML", "RF"],
                    skiprows=3,
                )

    df = df[df["Date"].apply(lambda x: len(str(x).strip()) == 6)]
    df["Date"] = df["Date"].astype(str) + "01"
    df["Date"] = pd.to_datetime(df["Date"], format="%Y%m%d")
    df["MKT-RF"] = pd.to_numeric(df["MKT-RF"], downcast="float")
    df["SMB"] = pd.to_numeric(df["SMB"], downcast="float")
    df["HML"] = pd.to_numeric(df["HML"], downcast="float")
    df["RF"] = pd.to_numeric(df["RF"], downcast="float")
    df["MKT-RF"] = df["MKT-RF"] / 100
    df["SMB"] = df["SMB"] / 100
    df["HML"] = df["HML"] / 100
    df["RF"] = df["RF"] / 100
    df = df.set_index("Date")
    return df


@log_start_end(log=logger)
def get_historical_5(symbol: str) -> pd.DataFrame:
    """Get 5 year monthly historical performance for a ticker with dividends filtered

    Parameters
    ----------
    symbol : str
        A ticker symbol in string form

    Returns
    -------
    data : pd.DataFrame
        A dataframe with historical information
    """
    tick = yf.Ticker(symbol)
    df = tick.history(period="5y", interval="1mo")
    df = df[df.index.to_series().apply(lambda x: x.day == 1)]
    df = df.drop(["Dividends", "Stock Splits"], axis=1)
    df = df.dropna()
    df.index = [d.replace(tzinfo=None) for d in df.index]
    return df


@log_start_end(log=logger)
def capm_information(symbol: str) -> Tuple[float, float]:
    """Provides information that relates to the CAPM model

    Parameters
    ----------
    symbol : str
        A ticker symbol in string form

    Returns
    -------
    Tuple[float, float]
        The beta for a stock, The systematic risk for a stock
    """
    df_f = get_fama_raw()
    df_h = get_historical_5(symbol)
    df = df_h.join(df_f)
    df = df.dropna()
    df["Monthly Return"] = df["Close"].pct_change()
    df["Excess Monthly Return"] = df["Monthly Return"] - df["RF"]
    df["Excess MKT-RF"] = df["MKT-RF"] - df["RF"]
    df = df.dropna()
    y = df[["Excess Monthly Return"]]
    x = df["Excess MKT-RF"]
    x = sm.add_constant(x)
    model = sm.OLS(y, x).fit()
    beta = model.params["Excess MKT-RF"]
    sy = model.rsquared
    return beta, sy
