import pandas as pd
from prophet import Prophet
from datetime import datetime
import yfinance as yf
import numpy as np
import asyncio
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
#import matplotlib.pyplot as plt


async def download_data(ticker, start_date, end_date):
    try:
        df = yf.download(ticker, start=start_date, end=end_date, interval="1d")
        df = df.reset_index()
        df = df[['Date', 'Adj Close']]
        df = df.rename(columns={"Date": "ds", "Adj Close": "y"})
        if len(df) > 252*2: #At least 2 years of history is necessary
            #df['y'] = df['y'].rolling(window=200).mean()
            #df = df.dropna()
            return df
    except Exception as e:
    	print(e)


class PricePredictor:
    def __init__(self, predict_ndays=365):
    	self.predict_ndays = predict_ndays
    	self.model = Prophet(
            interval_width = 0.8,
            daily_seasonality=True,
            yearly_seasonality = True,
      		)

    def run(self, df):
    	self.model.fit(df)
    	future = self.model.make_future_dataframe(periods=self.predict_ndays)
    	forecast = self.model.predict(future)

    	# Apply rolling average to smooth the upper bound
    	rolling_window = 200
    	forecast['smoothed_upper'] = round(forecast['yhat_upper'].rolling(window=rolling_window, min_periods=1).mean(),2)
    	forecast['smoothed_lower'] = round(forecast['yhat_lower'].rolling(window=rolling_window, min_periods=1).mean(),2)
    	forecast['smoothed_mean'] = round(forecast['yhat'].rolling(window=rolling_window, min_periods=1).mean(),2)

    	actual_values = df['y'].values
    	predicted_values = forecast['yhat'].values[:-self.predict_ndays]

    	rmse = round(np.sqrt(mean_squared_error(actual_values, predicted_values)),2)
    	mape = round(np.mean(np.abs((actual_values - predicted_values) / actual_values)) * 100)
    	r2 = round(r2_score(actual_values, predicted_values)*100)

    	print("RMSE:", rmse)
    	print("MAPE:", mape)
    	print("R2 Score:", r2)
    	pred_date_list = forecast['ds'][-1200-self.predict_ndays:].dt.strftime('%Y-%m-%d').tolist()
    	upper_list = forecast['smoothed_upper'][-1200-self.predict_ndays:].tolist()
    	lower_list = forecast['smoothed_lower'][-1200-self.predict_ndays:].tolist()
    	mean_list = forecast['smoothed_mean'][-1200-self.predict_ndays:].tolist()

    	historical_date_list = df['ds'][-1200:].dt.strftime('%Y-%m-%d').tolist()
    	historical_price_list = round(df['y'][-1200:],2).tolist()

    	return {'rmse': rmse,'mape': mape,'r2Score':r2, 'historicalPrice': historical_price_list, 'predictionDate': pred_date_list, 'upperBand': upper_list, 'lowerBand': lower_list, 'meanResult': mean_list}



#Test Mode
async def main():
    for ticker in ['NVDA']:
        start_date = datetime(2000, 1, 1).strftime("%Y-%m-%d")
        end_date = datetime.today().strftime("%Y-%m-%d")
        df = await download_data(ticker, start_date, end_date)
        data = PricePredictor().run(df)
        print(data)

# Run the main function
#asyncio.run(main())




# Plotting
'''
fig, ax = plt.subplots(figsize=(10, 6))

ax.plot(forecast['ds'][-1200-predict_ndays:], forecast['smoothed_mean'][-1200-predict_ndays:], color='blue', label='Predicted')
ax.fill_between(forecast['ds'][-1200-predict_ndays:], forecast['smoothed_lower'][-1200-predict_ndays:], forecast['smoothed_upper'][-1200-predict_ndays:], color='gray', alpha=0.5, label='Confidence Interval')
ax.plot(df['ds'][-1200:], df['y'][-1200:], color='black', label='Actual')
ax.set_xlabel('Date')
ax.set_ylabel('Price')
ax.set_title('Forecasted Prices for {}'.format(ticker))
ax.legend()
ax.grid(True)
plt.show()
'''