# components/run_backtest.py
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px  

# Importing all necessary functions from utils, metrics, and plots
from utils import (
    download_data, 
    generate_recommendations, 
    backtest,  
    fetch_fama_french_factors, 
)

from metrics import (
    calculate_portfolio_metrics, 
    calculate_final_score, 
    calculate_risk_factor_attribution, 
    get_drawdown_details
)

from plots import (
    plot_growth_comparison, 
    plot_drawdown_comparison, 
    plot_cagr_over_time, 
    plot_allocation_pie, 
    plot_correlation_heatmap, 
    plot_risk_return_attribution, 
    plot_rolling_metrics
)

def run_backtest_ui():
    # Initialize portfolios in session state if not already initialized
    if 'portfolios' not in st.session_state:
        st.session_state.portfolios = []

    if st.session_state.portfolios:
        with st.form("compare_form"):
            st.subheader("🔍 Select Portfolios to Compare")
            comparison_type = st.selectbox(
                "Comparison Type",
                ["Portfolio vs Benchmark", "Portfolio vs Portfolio"]
            )
            portfolio_names = [p['name'] for p in st.session_state.portfolios]
            if comparison_type == "Portfolio vs Benchmark":
                portfolio1 = st.selectbox("Select Portfolio", portfolio_names)
            else:
                portfolio1 = st.selectbox("Select First Portfolio", portfolio_names, key="p1")
                portfolio2 = st.selectbox("Select Second Portfolio", portfolio_names, key="p2")

            st.subheader("⚙️ Visualizations Settings")
            selected_time_frames = st.multiselect(
                "Select Time Frames for CAGR",
                options=['Weekly', 'Monthly', 'Quarterly', 'Annually'],
                default=['Annually'],
                key="selected_time_frames",
                help="Choose one or more time frames to view CAGR over different horizons."
            )
            selected_rolling_periods = st.multiselect(
                "Select Rolling Periods (Days)",
                options=[30, 90, 180, 252],
                default=[252],
                key="selected_rolling_periods",
                help="Choose one or more periods to view rolling metrics."
            )

            run = st.form_submit_button("Run Backtest")

        if run:
            with st.spinner("Running backtest..."):
                if comparison_type == "Portfolio vs Benchmark":
                    try:
                        portfolio = next(p for p in st.session_state.portfolios if p['name'] == portfolio1)
                    except StopIteration:
                        st.error("Selected portfolio not found.")
                        return

                    price_data = download_data(
                        portfolio['selected'],
                        portfolio['start_date'],
                        portfolio['end_date']
                    )
                    if price_data.empty:
                        st.error("No price data available for the selected portfolio.")
                    else:
                        available_selected = [ticker for ticker in portfolio['selected'] if ticker in price_data.columns]
                        missing_selected = list(set(portfolio['selected']) - set(available_selected))
                        if missing_selected:
                            st.warning(f"Excluded tickers with no data: {', '.join(missing_selected)}")
                        if not available_selected:
                            st.error(
                                "No selected tickers have available data for the chosen date range. "
                                "Please ensure that the ticker symbols are correct and data is available for the specified period."
                            )
                        else:
                            allocations = [portfolio.get('allocations', [0.0]*len(portfolio.get('selected', [])))[i]
                                          for i, ticker in enumerate(portfolio.get('selected', [])) if ticker in available_selected]
                            if not np.isclose(sum(allocations), 100.0, atol=1e-4):
                                st.warning("Allocations do not sum to 100%. Adjusting allocations proportionally.")
                                allocations = [a / sum(allocations) * 100 for a in allocations]
                            weights = np.array(allocations) / 100
                            returns, cum_returns = backtest(weights, price_data[available_selected],
                                                            portfolio['rebalance_freq'], portfolio['broker_fee'], debug=False)
                            
                            # Ensure returns have a DateTime index
                            if not isinstance(returns.index, pd.DatetimeIndex):
                                try:
                                    returns.index = pd.to_datetime(price_data.index)
                                    cum_returns.index = pd.to_datetime(cum_returns.index)
                                except Exception as e:
                                    st.error(f"Error converting returns index to DateTimeIndex: {e}")
                                    return
                            
                            if cum_returns.empty:
                                st.error("Cumulative returns are empty. Check the data and allocations.")
                            else:
                                benchmark_symbol = portfolio['benchmark_symbol']
                                benchmark_data = download_data([benchmark_symbol], portfolio['start_date'], portfolio['end_date'])

                                if benchmark_symbol not in benchmark_data.columns:
                                    st.error(f"The selected benchmark symbol '{benchmark_symbol}' does not have available data for the chosen period.")
                                    st.stop()
                                else:
                                    st.success(f"Benchmark '{benchmark_symbol}' data successfully downloaded and will be used for comparison.")
                                    if not benchmark_data.empty:
                                        benchmark_returns = benchmark_data[benchmark_symbol].pct_change().dropna()
                                        benchmark_returns = benchmark_returns.reindex(returns.index, method='ffill').dropna()
                                        common_index = returns.index.intersection(benchmark_returns.index)
                                        if common_index.empty:
                                            st.error("No overlapping dates between portfolio returns and benchmark returns after alignment.")
                                            st.stop()
                                        returns = returns.loc[common_index]
                                        cum_returns = cum_returns.loc[common_index]
                                        # Align the data using an inner join
                                        aligned_data = pd.merge(returns.to_frame('Portfolio'), benchmark_returns.to_frame('Benchmark'), left_index=True, right_index=True, how='inner')

                                        # Check that we have data after merging
                                        if aligned_data.empty:
                                            st.error("No overlapping dates between portfolio returns and benchmark returns after alignment.")
                                            st.stop()

                                        # Assign aligned returns
                                        returns = aligned_data['Portfolio'].rename("Portfolio_Returns")
                                        benchmark_returns = aligned_data['Benchmark']
                                        benchmark_cum_returns = (1 + benchmark_returns).cumprod()
                                        benchmark_metrics = calculate_portfolio_metrics(
                                            benchmark_returns, benchmark_cum_returns, portfolio['rf_rate'], None
                                        )
                                    else:
                                        benchmark_returns = None
                                        benchmark_cum_returns = pd.Series(dtype=float)
                                        benchmark_metrics = {
                                            'Start Balance': "N/A",
                                            'End Balance': "N/A",
                                            'Annualized Return (CAGR)': "N/A",
                                            'Best Year': "N/A",
                                            'Worst Year': "N/A",
                                            'Arithmetic Mean (Monthly)': "N/A",
                                            'Arithmetic Mean (Annualized)': "N/A",
                                            'Geometric Mean (Monthly)': "N/A",
                                            'Geometric Mean (Annualized)': "N/A",
                                            'Standard Deviation (Monthly)': "N/A",
                                            'Standard Deviation (Annualized)': "N/A",
                                            'Downside Deviation (Monthly)': "N/A",
                                            'Maximum Drawdown': "N/A",
                                            'Sharpe Ratio': "N/A",
                                            'Sortino Ratio': "N/A",
                                            'Gain/Loss Ratio': "N/A",
                                            'Skewness': "N/A",
                                            'Excess Kurtosis': "N/A",
                                            'Safe Withdrawal Rate': "N/A",
                                            'Perpetual Withdrawal Rate': "N/A",
                                            'Positive Periods': "N/A",
                                            'Benchmark Correlation': "N/A",
                                            'Beta': "N/A",
                                            'Alpha (annualized)': "N/A",
                                            'R2': "N/A",
                                            'Treynor Ratio': "N/A",
                                            'Calmar Ratio': "N/A",
                                            'Modigliani–Modigliani Measure': "N/A",
                                            'Information Ratio': "N/A",
                                            'Tracking Error': "N/A",
                                            'Active Return': "N/A",
                                            'Upside Capture Ratio': "N/A",
                                            'Downside Capture Ratio': "N/A"
                                        }

                                if not cum_returns.empty:
                                    portfolio_metrics = calculate_portfolio_metrics(
                                        returns, cum_returns, portfolio['rf_rate'], benchmark_returns
                                    )

                                    # Dashboard Header with Key Stats
                                    with st.container():
                                        st.markdown("### 🔑 Key Metrics")
                                        key_metrics = {
                                            'Annualized Return (CAGR)': portfolio_metrics.get('Annualized Return (CAGR)', "N/A"),
                                            'Sharpe Ratio': portfolio_metrics.get('Sharpe Ratio', "N/A"),
                                            'Maximum Drawdown': portfolio_metrics.get('Maximum Drawdown', "N/A")
                                        }
                                        cols = st.columns(len(key_metrics))
                                        for col, (metric, value) in zip(cols, key_metrics.items()):
                                            with col:
                                                st.metric(label=metric, value=value)

                                    # Create Tabs for Organized Sections
                                    tabs = st.tabs(["Overview", "Performance Statistics", "Advanced Metrics", "Drawdowns", "Visualizations"])

                                    with tabs[0]:
                                        st.header("📈 Portfolio Performance Overview")
                                        st.write(f"**Portfolio Name:** {portfolio['name']}")
                                        st.write(f"**Start Date:** {portfolio['start_date'].strftime('%Y-%m-%d')}")
                                        st.write(f"**End Date:** {portfolio['end_date'].strftime('%Y-%m-%d')}")
                                        st.write(f"**Benchmark:** {portfolio['benchmark_symbol']}")

                                    with tabs[1]:
                                        st.header("📊 Performance Statistics")
                                        
                                        with st.container():
                                            st.subheader("🛠️ Basic Metrics")
                                            performance_data_stats = {
                                                'Metric': [
                                                    'Start Balance',
                                                    'End Balance',
                                                    'Annualized Return (CAGR)',
                                                    'Standard Deviation (Annualized)',
                                                    'Best Year',
                                                    'Worst Year',
                                                    'Maximum Drawdown',
                                                    'Sharpe Ratio',
                                                    'Sortino Ratio',
                                                    'Benchmark Correlation'
                                                ],
                                                'Portfolio': [
                                                    portfolio_metrics['Start Balance'],
                                                    portfolio_metrics['End Balance'],
                                                    portfolio_metrics['Annualized Return (CAGR)'],
                                                    portfolio_metrics['Standard Deviation (Annualized)'],
                                                    portfolio_metrics['Best Year'],
                                                    portfolio_metrics['Worst Year'],
                                                    portfolio_metrics['Maximum Drawdown'],
                                                    portfolio_metrics['Sharpe Ratio'],
                                                    portfolio_metrics['Sortino Ratio'],
                                                    portfolio_metrics['Benchmark Correlation']
                                                ],
                                                'Benchmark': [
                                                    benchmark_metrics['Start Balance'],
                                                    benchmark_metrics['End Balance'],
                                                    benchmark_metrics['Annualized Return (CAGR)'],
                                                    benchmark_metrics['Standard Deviation (Annualized)'],
                                                    benchmark_metrics['Best Year'],
                                                    benchmark_metrics['Worst Year'],
                                                    benchmark_metrics['Maximum Drawdown'],
                                                    benchmark_metrics['Sharpe Ratio'],
                                                    benchmark_metrics['Sortino Ratio'],
                                                    benchmark_metrics['Benchmark Correlation']
                                                ]
                                            }
                                            performance_df_stats = pd.DataFrame(performance_data_stats).set_index('Metric')
                                            st.table(performance_df_stats)

                                    with tabs[2]:
                                        st.header("📋 Detail Comparisons")
                                        with st.container():
                                            st.subheader("🧮 Advanced Metrics")
                                            performance_data_advanced = {
                                                'Metric': list(portfolio_metrics.keys()),
                                                'Portfolio': list(portfolio_metrics.values()),
                                                'Benchmark': list(benchmark_metrics.values())
                                            }
                                            performance_df_advanced = pd.DataFrame(performance_data_advanced).set_index('Metric')
        
                                            # Replace NaN with empty strings for better visualization
                                            performance_df_advanced = performance_df_advanced.replace(np.nan, "")
                                            # Drop rows where both Portfolio and Benchmark values are NaN
                                            performance_df_advanced_clean = performance_df_advanced.dropna(how='all')
        
                                            st.table(performance_df_advanced_clean)
                                            st.markdown("---")
                                            st.write("**Note:** Some metrics in the benchmark column are empty because they are portfolio-specific measurements that can't be calculated for the benchmark alone. These include:\n\n"
             "1. Alpha (annualized)\n"
             "2. Information Ratio\n"
             "3. Tracking Error\n"
             "4. Active Return\n"
             "5. Upside/Downside Capture Ratios\n\n"
             "These metrics specifically measure how a portfolio performs relative to its benchmark, so they only make sense when calculated for the portfolio itself. For example, tracking error measures how closely a portfolio follows its benchmark, which isn't applicable to the benchmark itself. Similarly, alpha measures excess return relative to the benchmark, which wouldn't be meaningful to calculate for the benchmark itself.")
                                        st.subheader("🔍 Risk Factor Attribution Analysis")
                                        
                                        # Ensure 'returns' is defined and is a pd.Series
                                        if 'returns' not in locals():
                                            st.error("Portfolio returns data ('returns') is not defined.")
                                        else:
                                            # Define date range based on portfolio returns index
                                            start_date = returns.index.min().strftime('%Y-%m-%d')
                                            end_date = returns.index.max().strftime('%Y-%m-%d')
                                            # Fetch actual factor data
                                            factors = fetch_fama_french_factors(start_date, end_date)
                                            # Ensure factors are fetched
                                            if factors.empty:
                                                st.write("Failed to retrieve factor data.")       
                                            else:
                                                if isinstance(returns.index, pd.DatetimeIndex):
                                                    if returns.index.freq is None:
                                                        inferred_freq = pd.infer_freq(returns.index)
                                                        if inferred_freq is None:
                                                            st.error("Cannot infer frequency of returns index.")
                                                            st.stop()
                                                    else:
                                                        inferred_freq = returns.index.freq
                                                    try:
                                                        factors = factors.asfreq(inferred_freq, method='ffill')
                                                    except TypeError as e:
                                                        st.error(f"Error setting frequency for factors: {e}")
                                                        st.stop()
                                                else:
                                                    st.error("Returns index is not a DatetimeIndex.")
                                                    st.stop()
                                                # Calculate Attribution with aligned dates
                                                attribution, error = calculate_risk_factor_attribution(returns, factors)
                                                
                                                if error:
                                                    st.error(error)
                                                    st.write("Risk factor attribution analysis is unavailable.")
                                                elif attribution.empty:
                                                    st.write("Risk factor attribution analysis is unavailable.")
                                                else:
                                                    st.write("Risk Factor Attribution:")
                                                    st.table(attribution)
                                                    # Optional: Visualize Attribution
                                                    fig_attribution = px.bar(
                                                        attribution,
                                                        x='Factor',
                                                        y='Contribution (%)',
                                                        title='Risk Factor Attribution',
                                                        labels={'Contribution (%)': 'Contribution (%)'},
                                                        template='plotly_dark'
                                                    )
                                                    st.plotly_chart(fig_attribution, use_container_width=True)

                                    with tabs[3]:
                                        st.header("📉 Detailed Drawdowns")
                                        st.markdown("### 📈 Drawdowns for Portfolio")
                                        portfolio_drawdowns = get_drawdown_details(cum_returns)
                                        if portfolio_drawdowns:
                                            portfolio_drawdowns_df = pd.DataFrame(portfolio_drawdowns)
                                            st.table(portfolio_drawdowns_df)
                                        else:
                                            st.write("No drawdowns detected for the portfolio.")

                                        if benchmark_returns is not None and not benchmark_returns.empty:
                                            st.markdown("### 📈 Drawdowns for Benchmark")
                                            benchmark_drawdowns = get_drawdown_details(benchmark_cum_returns)
                                            if benchmark_drawdowns:
                                                benchmark_drawdowns_df = pd.DataFrame(benchmark_drawdowns)
                                                st.table(benchmark_drawdowns_df)
                                            else:
                                                st.write("No drawdowns detected for the benchmark.")
                                        else:
                                            st.write("Benchmark data not available for drawdown analysis.")

                                    with tabs[4]:
                                        st.header("📊 Visualizations")
                                        if not cum_returns.empty and not benchmark_cum_returns.empty:
                                            plot_growth_comparison(cum_returns, benchmark_cum_returns)
                                        else:
                                            st.warning("Insufficient data to display Growth Comparison. Ensure both portfolio and benchmark have data.")
                                        
                                        portfolio_drawdown_series = (cum_returns / cum_returns.expanding().max() - 1) * 100
                                        benchmark_drawdown_series = (benchmark_cum_returns / benchmark_cum_returns.expanding().max() - 1) * 100
                                        plot_drawdown_comparison(portfolio_drawdown_series, benchmark_drawdown_series)

                                        st.subheader("📈 Compound Annual Growth Rate (CAGR) Over Time")
                                        if selected_time_frames:
                                            plot_cagr_over_time(cum_returns, time_frames=selected_time_frames)
                                        else:
                                            st.warning("Please select at least one time frame for CAGR.")

                                        # Box Plot for Returns Distribution
                                        st.subheader("📦 Returns Distribution Box Plot")
                                        if isinstance(returns, pd.DataFrame):
                                            melted_returns = returns.reset_index().melt(id_vars='Date', var_name='Asset', value_name='Return')
                                            fig_box_plot = px.box(
                                                melted_returns,
                                                x='Asset',
                                                y='Return',
                                                title='Returns Distribution Box Plot',
                                                labels={'Return': 'Returns', 'Asset': 'Asset'},
                                                points='all',
                                                hover_data=['Return'],
                                                template='plotly_dark',
                                                color='Asset',
                                                color_discrete_sequence=px.colors.qualitative.Set3
                                            )
                                            st.plotly_chart(fig_box_plot, use_container_width=True)
                                            st.markdown("**Interpretation:** The box plot visualizes the distribution of returns for each asset in the portfolio. The boxes represent the interquartile range (IQR), the line inside the box indicates the median, and the whiskers show the range of the data. Outliers are displayed as individual points.")
                                        elif isinstance(returns, pd.Series):
                                            fig_box_plot = px.box(
                                                returns.to_frame(name='Return'),
                                                y='Return',
                                                title='Returns Distribution Box Plot',
                                                labels={'Return': 'Returns'},
                                                points='all',
                                                hover_data=['Return'],
                                                template='plotly_dark'
                                            )
                                            st.plotly_chart(fig_box_plot, use_container_width=True)
                                            st.markdown("**Interpretation:** The box plot visualizes the distribution of returns for the portfolio. The box represents the interquartile range (IQR), the line inside the box indicates the median, and the whiskers show the range of the data. Outliers are displayed as individual points.")
                                        else:
                                            st.warning("Returns data is neither a DataFrame nor a Series.")

                                        # Heatmap of Correlations Between Assets
                                        st.subheader("🔥 Correlation Heatmap of Assets")
                                        plot_correlation_heatmap(returns)

                                        # Add Risk-Return Attribution Analysis
                                        st.subheader("🔍 Risk-Return Attribution Analysis")
                                        plot_risk_return_attribution(returns, weights)

                                        # Cumulative Returns Heatmap
                                        st.subheader("🔥 Cumulative Returns Heatmap")
                                        try:
                                            cum_returns_normalized = cum_returns / cum_returns.max()
                                            fig_cum_heatmap = px.imshow(
                                                cum_returns_normalized.to_frame().T,
                                                labels=dict(x="Date", y="Portfolio", color="Normalized Cumulative Return"),
                                                title="Cumulative Returns Heatmap",
                                                aspect="auto",
                                                color_continuous_scale='Viridis',
                                                template='plotly_dark'
                                            )
                                            st.plotly_chart(fig_cum_heatmap, use_container_width=True)
                                            st.markdown("**Interpretation:** The heatmap visualizes the normalized cumulative returns over time, allowing for an intuitive comparison of portfolio performance across different periods.")
                                        except Exception as e:
                                            st.error(f"Error plotting cumulative returns heatmap: {e}")

                                        # Portfolio Allocation Pie Chart
                                        st.subheader("🥧 Portfolio Allocation Pie Chart")
                                        plot_allocation_pie(weights, available_selected, title="Portfolio Allocation", hover_info="percent+name")

                                        # Rolling Metrics Chart with User-Defined Periods
                                        st.subheader("📉 Rolling Metrics")
                                        if selected_rolling_periods:
                                            plot_rolling_metrics(returns, windows=selected_rolling_periods)
                                        else:
                                            st.warning("Please select at least one rolling period to display metrics.")

                                        # Recommendations
                                        st.markdown("### 💡 Recommendations")
                                        recommendations = generate_recommendations(
                                            allocations=[alloc for alloc in portfolio['allocations'] if alloc > 0],
                                            available_selected=[ticker for ticker in available_selected if portfolio['allocations'][available_selected.index(ticker)] > 0]
                                        )
                                        
                                        if recommendations:
                                            for rec in recommendations:
                                                st.write(rec)
                                        else:
                                            st.success("Your portfolio allocations are well-balanced!")
                                            
                                        # Final Score
                                        st.markdown("### 📝 Final Score")
                                        portfolio_score = calculate_final_score(portfolio_metrics, benchmark_metrics)
                                        benchmark_score = calculate_final_score(benchmark_metrics, portfolio_metrics)
                                        st.write(f"**Portfolio:** {portfolio_score:.2f} / 100")
                                        st.write(f"**Benchmark:** {benchmark_score:.2f} / 100")

                                        # Store results
                                        st.session_state.backtest_results = {
                                            'returns': returns,
                                            'cum_returns': cum_returns,
                                            'weights_a': weights,
                                            'price_data': price_data[available_selected],
                                            'metrics': portfolio_metrics,
                                            'benchmark_metrics': benchmark_metrics,
                                            'performance_df_stats': performance_df_stats,
                                            'performance_df_advanced': benchmark_metrics,  # Adjust as necessary
                                            'benchmark_cum_returns': benchmark_cum_returns
                                        }
                st.success("Backtest completed successfully!")
    else:
        st.warning("Please add at least one portfolio to run backtest.")

def calculate_risk_factor_attribution(returns, factors, annualization_factor=252):
    """
    Decompose portfolio returns by risk factors using linear regression and provide
    comments on the factor contributions.

    Parameters:
    - returns (pd.Series): Portfolio returns.
    - factors (pd.DataFrame): DataFrame where each column represents a risk factor.
    - annualization_factor (int): Annualization factor (252 for daily, 12 for monthly returns).

    Returns:
    - pd.DataFrame: Factor names, their contribution percentages, and explanatory comments.
    - str: Error message if any exception occurs, else None.
    """
    try:
        # Ensure returns has a name, or assign one if missing
        if returns.name is None:
            returns = returns.rename("Portfolio_Returns")
        
        # Convert returns to a DataFrame
        returns = returns.to_frame()
        
        # Ensure both returns and factors are in decimal format
        if returns['Portfolio_Returns'].max() > 1:
            returns = returns / 100
            
        if factors.max().max() > 1:
            factors = factors / 100
        
        # Adjust returns to excess returns by subtracting RF (if it exists in factors)
        if 'RF' in factors.columns:
            returns['Portfolio_Returns'] = returns['Portfolio_Returns'] - factors['RF']
            factors = factors.drop(columns=['RF'])
        
        # Align the data using an inner join and drop any remaining NaN values
        aligned_data = pd.merge(returns, factors, left_index=True, right_index=True, how='inner').dropna()

        # Check that we have data after merging
        if aligned_data.empty:
            raise ValueError("No overlapping dates between returns and factors after dropping NaNs.")
    
        # Prepare X and y for regression
        X = aligned_data[factors.columns]
        X = sm.add_constant(X)  # Add intercept
        y = aligned_data['Portfolio_Returns']
        
        # Run the regression model
        model = sm.OLS(y, X).fit()
        coefficients = model.params.drop('const', errors='ignore')
        
        # Calculate factor contributions (non-annualized)
        factor_means = X.mean().drop('const', errors='ignore')
        contributions = coefficients * factor_means
        
        # Calculate total return
        total_return = returns['Portfolio_Returns'].mean()

        # Check if total_return is zero to avoid division by zero
        if np.isclose(total_return, 0):
            attribution_df = pd.DataFrame({
                'Factor': contributions.index,
                'Contribution (%)': [0.0] * len(contributions)
            })
        else:
            # Calculate percentage contributions
            attribution_df = pd.DataFrame({
                'Factor': contributions.index,
                'Contribution (%)': (contributions / total_return * 100).round(2)
            })
        
        # Remove the constant term if it exists
        if 'const' in attribution_df['Factor'].values:
            attribution_df = attribution_df[attribution_df['Factor'] != 'const']
        
        # Use full names for factors
        factor_full_names = {
            'Mkt-RF': 'Market Risk Premium',
            'SMB': 'Small Minus Big',
            'HML': 'High Minus Low'
        }
        attribution_df['Factor'] = attribution_df['Factor'].replace(factor_full_names)
        
        # Generate detailed comments based on factor contributions
        comments = []
        for _, row in attribution_df.iterrows():
            factor_name = row['Factor']
            contribution = row['Contribution (%)']

            # Market Risk Premium (Mkt-RF) Analysis
            if factor_name == 'Market Risk Premium':
                if contribution > 75:
                    comments.append(f"{factor_name} = {contribution}%: Extremely high sensitivity to market movements.")
                elif contribution > 50:
                    comments.append(f"{factor_name} = {contribution}%: High sensitivity to overall market movements.")
                elif contribution > 30:
                    comments.append(f"{factor_name} = {contribution}%: Notable sensitivity to market risk.")
                elif contribution > 10:
                    comments.append(f"{factor_name} = {contribution}%: Moderate exposure to market risk.")
                else:
                    comments.append(f"{factor_name} = {contribution}%: Low exposure to market risk.")

            # Small Minus Big (SMB) Analysis
            elif factor_name == 'Small Minus Big':
                if contribution > 5:
                    comments.append(f"{factor_name} = {contribution}%: Strong tilt towards small-cap stocks.")
                elif contribution > 0:
                    comments.append(f"{factor_name} = {contribution}%: Mild preference for small-cap stocks.")
                elif contribution > -5:
                    comments.append(f"{factor_name} = {contribution}%: Slight tilt towards larger-cap stocks.")
                else:
                    comments.append(f"{factor_name} = {contribution}%: Clear preference for large-cap stocks.")

            # High Minus Low (HML) Analysis
            elif factor_name == 'High Minus Low':
                if contribution > 5:
                    comments.append(f"{factor_name} = {contribution}%: Strong tilt toward value stocks.")
                elif contribution > 0:
                    comments.append(f"{factor_name} = {contribution}%: Slight positive tilt toward value stocks.")
                elif contribution > -5:
                    comments.append(f"{factor_name} = {contribution}%: Minor negative tilt toward growth stocks.")
                else:
                    comments.append(f"{factor_name} = {contribution}%: Clear preference for growth stocks.")

        # Add comments to the DataFrame
        attribution_df['Comment'] = comments
    
        return attribution_df, None
    
    except Exception as e:
        error_message = f"Error calculating risk factor attribution: {e}"
        return pd.DataFrame(), error_message


Risk Factor Attribution Analysis
Cannot infer frequency of returns index.

fix it,
Please review my code and provide the original code alongside the updated one. Include the exact instructions for where and how to apply the corrections in the original code by referencing specific function names, variable names, or line numbers if possible. Dont give me general corrections as I am a newbie in coding, so make your response concise and simple, showing only the revised code snippets with a brief explanation for each change.