import gradio as gr
from df.PaperCentral import PaperCentral
from gradio_calendar import Calendar
from datetime import datetime, timedelta
from typing import Union, List, Optional, Tuple
from author_leaderboard_tab import author_leaderboard_tab
from pr_paper_central_tab import pr_paper_central_tab
from huggingface_hub import whoami
import json
import requests
from author_leaderboard_contrib_tab import author_resource_leaderboard_tab
from zoneinfo import ZoneInfo  # Available in Python 3.9 and later
# Initialize the PaperCentral class instance
paper_central_df = PaperCentral()
def logging_flow(oauth_token: Optional[gr.OAuthToken]):
    if oauth_token is None:
        # User is not logged in
        visibility_update = gr.update(visible=True)
        user_info_update = gr.update(value="", label="User")
        return visibility_update, user_info_update
    try:
        # Attempt to get user information
        user_info = whoami(oauth_token.token)
        visibility_update = gr.update(visible=False)
    except requests.exceptions.HTTPError as e:
        # If there's an error (e.g., token is invalid), treat as not logged in
        visibility_update = gr.update(visible=True)
        user_info_update = gr.update(value="", label="User")
        return visibility_update, user_info_update
    # Prepare user information for display
    avatar_url = user_info.get("avatarUrl", "")
    name = user_info.get("name", "")
    avatar_html = f'''
    
   
        
        {name}
     
    Head to "Edit papers" tab to modify your paper!
    '''
    user_info_update = gr.update(value=avatar_html, label="User")
    return visibility_update, user_info_update
# Create the Gradio Blocks app with custom CSS
with gr.Blocks(css="style.css") as demo:
    gr.Markdown("# Paper Central")
    with gr.Row():
        with gr.Column(scale=1):
            login_button = gr.LoginButton(value="Add or edit your papers")
            user_info_md = gr.HTML()
        with gr.Column(scale=1):
            with gr.Accordion(label="⭐Release notes", open=False):
                gr.Markdown("""
                - **October 24, 2024** – CoRL 2024 proceedings added.
                - **October 20, 2024** – You can now add or edit papers.
                - **October 19, 2024** – Papers with github now have github stars.
                - **October 16, 2024** – Added functionality to filter papers by date ranges.
                - **October 11, 2024** – Introduced leaderboards feature.
                - **October 8, 2024** – MICCAI 2024 proceedings added.
                - **October 7, 2024** – COLM 2024 proceedings added.
                - **October 4, 2024** – Added functionality to filter papers by title.
                """)
            gr.Button(value="Use paper-central in datasets",
                      icon="https://huggingface.co/front/assets/huggingface_logo-noborder.svg",
                      size="sm",
                      link="https://huggingface.co/datasets/huggingface/paper-central-data")
    with gr.Tabs() as tabs:
        with gr.Tab("Paper-central", id="tab-paper-central"):
            # Create a row for navigation buttons and calendar
            with gr.Row():
                with gr.Column(scale=1):
                    # Define the 'Next Day' and 'Previous Day' buttons
                    next_day_btn = gr.Button("Next Day")
                    prev_day_btn = gr.Button("Previous Day")
                with gr.Column(scale=4):
                    # Define the calendar component for date selection
                    calendar = Calendar(
                        type="datetime",
                        label="Select a date",
                        info="Click the calendar icon to bring up the calendar.",
                        value=datetime.now(ZoneInfo('America/Los_Angeles')).strftime('%Y-%m-%d')
                        # Default to today's date in PST
                    )
                    # Add Radio buttons for date ranges
                    date_range_radio = gr.Radio(
                        label="Date Range",
                        choices=["This week", "This month", "This year", "All time"],
                        value=None,
                    )
            # Create a row for Hugging Face options and Conference options
            with gr.Row():
                with gr.Column():
                    # Define the checkbox group for Hugging Face options
                    cat_options = gr.CheckboxGroup(
                        label="Category",
                        choices=[
                            'cs.*',
                            'eess.*',
                            'econ.*',
                            'math.*',
                            'astro-ph.*',
                            'cond-mat.*',
                            'gr-qc',
                            'hep-ex',
                            'hep-lat',
                            'hep-ph',
                            'hep-th',
                            'math-ph',
                            'nlin.*',
                            'nucl-ex',
                            'nucl-th',
                            'physics.*',
                            'quant-ph',
                            'q-bio.*',
                            'q-fin.*',
                            'stat.*',
                        ],
                        value=["cs.*"]
                    )
                    hf_options = gr.CheckboxGroup(
                        label="Hugging Face options",
                        choices=["🤗 artifacts", "datasets", "models", "spaces", "github", "project page"],
                        value=[],
                        elem_id="hf_options"
                    )
                with gr.Column():
                    # Define the checkbox group for Conference options
                    conference_options = gr.CheckboxGroup(
                        label="Conference options",
                        choices=["ALL"] + PaperCentral.CONFERENCES
                    )
                    with gr.Row():
                        # Define a Textbox for author search
                        author_search = gr.Textbox(
                            label="Search Authors",
                            placeholder="Enter author name",
                        )
                        title_search = gr.Textbox(
                            label="Search Title",
                            placeholder="Enter keywords",
                        )
            # Define the Dataframe component to display paper data
            # List of columns in your DataFrame
            columns = paper_central_df.COLUMNS_START_PAPER_PAGE
            paper_central_component = gr.Dataframe(
                label="Paper Data",
                value=paper_central_df.df_prettified[columns],
                datatype=[
                    paper_central_df.DATATYPES[column]
                    for column in columns
                ],
                row_count=(0, "dynamic"),
                interactive=False,
                height=1000,
                elem_id="table",
                wrap=True,
            )
        with gr.Tab("Edit papers", id="tab-pr"):
            pr_paper_central_tab(paper_central_df.df_raw)
        with gr.Tab("Leaderboards", id="tab-leaderboards"):
            with gr.Tab("Authors"):
                author_leaderboard_tab()
            with gr.Tab("Contributors"):
                author_resource_leaderboard_tab()
    # Define function to move to the next day
    def go_to_next_day(
            date: Union[str, datetime],
            cat_options_list: List[str],
            hf_options_list: List[str],
            conference_options_list: List[str],
            author_search_input: str,
            title_search_input: str,
            date_range_option: Optional[str],
    ) -> tuple:
        """
        Moves the selected date to the next day and updates the data.
        Returns:
            tuple: The new date as a string and the updated Dataframe component.
        """
        date_range_update = gr.update(value=None)
        # Ensure the date is in string format
        if isinstance(date, datetime):
            date_str = date.strftime('%Y-%m-%d')
        else:
            date_str = date
        # Parse the date string and add one day
        new_date = datetime.strptime(date_str, '%Y-%m-%d') + timedelta(days=1)
        new_date_str = new_date.strftime('%Y-%m-%d')
        # Update the Dataframe
        updated_data = paper_central_df.filter(
            selected_date=new_date_str,
            cat_options=cat_options_list,
            hf_options=hf_options_list,
            conference_options=conference_options_list,
            author_search_input=author_search_input,
            title_search_input=title_search_input,
            date_range_option=date_range_option,
        )
        # Return the new date and updated Dataframe
        return new_date_str, updated_data, date_range_update
    # Define function to move to the previous day
    def go_to_previous_day(
            date: Union[str, datetime],
            cat_options_list: List[str],
            hf_options_list: List[str],
            conference_options_list: List[str],
            author_search_input: str,
            title_search_input: str,
            date_range_option: Optional[str],
    ) -> tuple:
        """
        Moves the selected date to the previous day and updates the data.
        Returns:
            tuple: The new date as a string and the updated Dataframe component.
        """
        # If date_range_option is selected, do nothing
        date_range_update = gr.update(value=None)
        # Ensure the date is in string format
        if isinstance(date, datetime):
            date_str = date.strftime('%Y-%m-%d')
        else:
            date_str = date
        # Parse the date string and subtract one day
        new_date = datetime.strptime(date_str, '%Y-%m-%d') - timedelta(days=1)
        new_date_str = new_date.strftime('%Y-%m-%d')
        # Update the Dataframe
        updated_data = paper_central_df.filter(
            selected_date=new_date_str,
            cat_options=cat_options_list,
            hf_options=hf_options_list,
            conference_options=conference_options_list,
            author_search_input=author_search_input,
            title_search_input=title_search_input,
            date_range_option=date_range_option,
        )
        # Return the new date and updated Dataframe
        return new_date_str, updated_data, date_range_update
    # Define function to update data when date or options change
    def update_data(
            date: Union[str, datetime],
            cat_options_list: List[str],
            hf_options_list: List[str],
            conference_options_list: List[str],
            author_search_input: str,
            title_search_input: str,
            date_range_option: Optional[str],
    ):
        """
        Updates the data displayed in the Dataframe based on the selected date and options.
        """
        return paper_central_df.filter(
            selected_date=date,
            cat_options=cat_options_list,
            hf_options=hf_options_list,
            conference_options=conference_options_list,
            author_search_input=author_search_input,
            title_search_input=title_search_input,
            date_range_option=date_range_option,
        )
    # Function to handle conference options change
    def on_conference_options_change(
            date: Union[str, datetime],
            cat_options_list: List[str],
            hf_options_list: List[str],
            conference_options_list: List[str],
            author_search_input: str,
            title_search_input: str,
            date_range_option: Optional[str],
    ):
        cat_options_update = gr.update()
        paper_central_component_update = gr.update()
        visible = True
        # Some conference options are selected
        # Update cat_options to empty list
        if conference_options_list:
            cat_options_update = gr.update(value=[])
            paper_central_component_update = update_data(
                date,
                [],
                hf_options_list,
                conference_options_list,
                author_search_input,
                title_search_input,
                None,
            )
            visible = False
        calendar_update = gr.update(visible=visible)
        next_day_btn_update = gr.update(visible=visible)
        prev_day_btn_update = gr.update(visible=visible)
        date_range_option_update = gr.update(visible=visible, value=None)
        return paper_central_component_update, cat_options_update, calendar_update, next_day_btn_update, prev_day_btn_update, date_range_option_update
    # Function to handle category options change
    def on_cat_options_change(
            date: Union[str, datetime],
            cat_options_list: List[str],
            hf_options_list: List[str],
            conference_options_list: List[str],
            author_search_input: str,
            title_search_input: str,
            date_range_option: Optional[str],
    ):
        conference_options_update = gr.update()
        paper_central_component_update = gr.update()
        visible = False
        # Some category options are selected
        # Update conference_options to empty list
        if cat_options_list:
            conference_options_update = gr.update(value=[])
            paper_central_component_update = update_data(
                date,
                cat_options_list,
                hf_options_list,
                [],
                author_search_input,
                title_search_input,
                date_range_option,
            )
            visible = True
        calendar_update = gr.update(visible=visible)
        next_day_btn_update = gr.update(visible=visible)
        prev_day_btn_update = gr.update(visible=visible)
        date_range_option_update = gr.update(visible=visible)
        return paper_central_component_update, conference_options_update, calendar_update, next_day_btn_update, prev_day_btn_update, date_range_option_update
    # Include date_range_radio in the inputs
    inputs = [
        calendar,
        cat_options,
        hf_options,
        conference_options,
        author_search,
        title_search,
        date_range_radio,
    ]
    # Set up the event listener for the author search
    author_search.submit(
        fn=update_data,
        inputs=inputs,
        outputs=paper_central_component,
        api_name=False
    )
    title_search.submit(
        fn=update_data,
        inputs=inputs,
        outputs=paper_central_component,
        api_name=False
    )
    # Set up the event listener for the 'Next Day' button
    next_day_btn.click(
        fn=go_to_next_day,
        inputs=inputs,
        outputs=[calendar, paper_central_component, date_range_radio],
        api_name=False
    )
    # Set up the event listener for the 'Previous Day' button
    prev_day_btn.click(
        fn=go_to_previous_day,
        inputs=inputs,
        outputs=[calendar, paper_central_component, date_range_radio],
        api_name=False
    )
    # Set up the event listener for the calendar date change
    calendar.change(
        fn=update_data,
        inputs=inputs,
        outputs=paper_central_component,
        api_name=False
    )
    # Set up the event listener for the Hugging Face options change
    hf_options.change(
        fn=update_data,
        inputs=inputs,
        outputs=paper_central_component,
        api_name=False
    )
    # Event chaining for conference options change
    conference_options.change(
        fn=on_conference_options_change,
        inputs=inputs,
        outputs=[paper_central_component, cat_options, calendar, next_day_btn, prev_day_btn, date_range_radio],
        api_name=False
    )
    # Event chaining for category options change
    cat_options.change(
        fn=on_cat_options_change,
        inputs=inputs,
        outputs=[paper_central_component, conference_options, calendar, next_day_btn, prev_day_btn, date_range_radio],
        api_name=False
    )
    # Set up the event listener for the date range radio button change
    date_range_radio.change(
        fn=update_data,
        inputs=inputs,
        outputs=paper_central_component,
        api_name=False
    )
    # Load the initial data when the app starts
    request = gr.Request()
    def echo(request: gr.Request):
        """
        Echoes the input text and prints request details.
        Args:
            text (str): The input text from the user.
            request (gr.Request): The HTTP request object.
        Returns:
            str: The echoed text.
        """
        calendar = gr.update(datetime.today().strftime('%Y-%m-%d'))
        date_range = gr.update(value=[])
        conferences = gr.update(value=[])
        hf_options = gr.update(value=[])
        leaderboard_tab = gr.Tabs()
        if request:
            print("Request headers dictionary:", dict(request.headers))
            print("IP address:", request.client.host)
            print("Query parameters:", dict(request.query_params))
            print("Session hash:", request.session_hash)
            if 'date' in request.query_params:
                calendar = gr.update(value=request.query_params['date'])
            if 'date_range' in request.query_params:
                date_range = gr.update(value=request.query_params['date_range'])
            if 'conferences' in request.query_params:
                conferences = request.query_params['conferences']
                try:
                    conferences = [value for value in conferences.split(',')]
                except ValueError:
                    conferences = []
                conferences = gr.update(value=conferences)
            if "hf_options" in request.query_params:
                hf_options = request.query_params['hf_options']
                try:
                    hf_options = [value for value in hf_options.split(',')]
                except ValueError:
                    hf_options = []
                hf_options = gr.update(value=hf_options)
            if "tab" in request.query_params:
                tab = request.query_params['tab']
                if tab == "tab-leaderboards":
                    leaderboard_tab = gr.Tabs(selected="tab-leaderboards")
        return calendar, date_range, conferences, hf_options, leaderboard_tab,
    demo.load(
        fn=update_data,
        inputs=inputs,
        outputs=paper_central_component,
        api_name="update_data",
    ).then(
        fn=echo,
        outputs=[calendar, date_range_radio, conference_options, hf_options, tabs],
        api_name=False,
    ).then(
        # New then to handle LoginButton and HTML components
        fn=logging_flow,
        outputs=[login_button, user_info_md],
        api_name=False,
    )
# Define the main function to launch the app
def main():
    """
    Launches the Gradio app.
    """
    demo.launch()
# Run the main function when the script is executed
if __name__ == "__main__":
    main()