import pandas as pd
import numpy as np
from pathlib import Path
from .constants import DATA_DIR
import sys


DEFAULT_XLSX_PATH = DATA_DIR / "example_conversation.xlsx"
DEFAULT_FALLBACK_MESSAGE = "Please try again"
DEFAULT_AFFIRMATION = "Good"


# Read spreadsheet and convert to dataframe
def read_and_preprocess_spreadsheet(path):
    # worksheet = gc.open('Test-ActiveListening1').sheet1
    df = pd.read_excel(path, engine="openpyxl")
    df = df.fillna(0)
    script_df = df.replace("", 0)
    script_df["Go to Row Number"] = script_df["Go to Row Number"].apply(np.int64)
    script_df.head()

    return script_df


def TerminalCard(row_num, text=None, next_flow=None):
    message = ""
    if text:
        message = f'''text("""
  {text}
  """)'''

    link = ""
    if next_flow:
        link = f'run_stack("{next_flow}")'

    terminalCardTemplate = f"""
card Row{row_num} do
  {message}
  
  {link}
end    
    """
    return terminalCardTemplate


def ContinuingCard(row_num, go_to_row_num, text=None, next_flow=None):
    message = ""
    if text and text != "":
        message = f'''text("""
  {text}
  """)'''

    link = ""
    if next_flow:
        link = f'run_stack("{next_flow}")'

    continuingCardTemplate = f"""
card Row{row_num}, then: Row{go_to_row_num} do
  {message}
  
  {link}
end
    """
    return continuingCardTemplate


def ButtonCard(
    row_num,
    go_to_row_nums,
    text,
    fallback=None,
    variable_name=None,
    variable_action=None,
):
    # Convert the array of strings to a long string...
    # ...so that Elixir recognizes variables instead of strings
    str = ", ".join(go_to_row_nums)

    fallback_go_to = ""
    fallback_boolean = ""
    if fallback == "Pass":
        fallback_go_to = f", then: {go_to_row_nums[0]}"
    elif fallback == "Reprompt":
        fallback_go_to = f", then: Row{row_num}Fallback"
        fallback_boolean = "fallback = True"

    set_variable = """"""
    if variable_name:
        if variable_name != None and variable_action != None:
            var_name_arr = variable_name.split(",")
            var_action_arr = variable_action.split(",")
            if len(var_name_arr) == len(var_action_arr):
                for i in range(len(var_name_arr)):
                    action = "= 0"
                    if var_action_arr[i] == "Set 0":
                        action = "= 0"
                    set_variable += f"""
    {var_name_arr[i]} {action}
    """

    buttonCardTemplate = f'''
card Row{row_num} {fallback_go_to} do
  {set_variable}
  buttons([{str}]) do
    text("""
    {text}
    """)
  end
  {fallback_boolean}
end    
'''
    return buttonCardTemplate


def ButtonOption(
    row_num,
    button_text,
    go_to_row_num,
    link=None,
    variable_name=None,
    variable_action=None,
):
    content = 'log("Placeholder")'
    if link:
        content = f'run_stack("{link}")'

    go_to = f", then: Row{go_to_row_num}"
    if go_to_row_num == 0:
        go_to = ""

    variable = ""
    set_variable = ""
    if variable_name:
        if variable_action == 0 or variable_action is None:
            variable = f'update_contact({variable_name}: "@response")'
        elif variable_action == "Add 1":
            set_variable = f"{variable_name} = {variable_name} + 1"
        elif variable_action == "Set 0":
            set_variable = f"""
            {variable_name} = {variable_name} + 0
            {variable_name} = {variable_name} + 0
            """
    buttonOptionTemplate = f"""
card Row{row_num}, "{button_text}" {go_to} do
  {set_variable}

  {content}
  
  {variable}
end
    """
    return buttonOptionTemplate


def SavedInputCard(row_num, go_to_row_num, prompt, variable_name):
    savedInputCardTemplate = f'''
card Row{row_num}, then: Row{go_to_row_num} do
  response =
    ask("""
    {prompt}
    """)
  update_contact({variable_name}: "@response")
end
'''
    return savedInputCardTemplate


def UnsavedInputCard(row_num, go_to_row_num, prompt, fallback):
    if fallback:
        next_card = f"Row{row_num}Manager"
    else:
        next_card = f"Row{go_to_row_num}"
    unsavedInputCardTemplate = f'''
card Row{row_num}, then: {next_card} do
  response = ask("""
  {prompt}
  """)
end
'''
    return unsavedInputCardTemplate


def ImageCardWithText(row_num, go_to_row_num, image_link, text):
    ImageTemplate = f'''
card Row{row_num}, then: Row{go_to_row_num} do
  image("{image_link}")

  text("""
  {text}
  """)
end
    '''
    return ImageTemplate


def ImageCardWithButtons(row_num, go_to_row_nums, image_link, text, fallback):
    fallback_boolean = ""
    fallback_go_to = ""
    if fallback == "Pass":
        fallback_go_to = f", then: {go_to_row_nums[0]}"
    elif fallback == "Reprompt":
        fallback_go_to = f", then: Row{row_num}Fallback"
        fallback_boolean = "fallback = True"

    str = ", ".join(go_to_row_nums)

    ImageTemplate = f'''   
card Row{row_num} {fallback_go_to} do
  buttons([{str}]) do
      image("{image_link}")
      text("""
      {text}
      """)
  end
  {fallback_boolean}
end
    '''
    return ImageTemplate


def SkipCard(row_num, go_to_row_num, skip_trigger, variable_name):
    trigger_arr = []

    if isinstance(skip_trigger, str):
        trigger_arr.append(f'response == "{skip_trigger.lower()}"')
        trigger_arr.append(f'response == "{skip_trigger.capitalize()}"')
        trigger_arr.append(f'response == "{skip_trigger.upper()}"')

        skip_triggers_positive = " or ".join(trigger_arr)

        negative_arr = [x.replace("==", "!=") for x in trigger_arr]

        skip_triggers_negative = " and ".join(negative_arr)

    SkipTemplate = f"""   
card Row{row_num} when {skip_triggers_positive}, then: Row{go_to_row_num} do
  log("Placeholder")
end

card Row{row_num} when {skip_triggers_negative}, then: Row{go_to_row_num} do
  log("Placeholder")
  
  update_contact({variable_name}: "@response")
end
    """
    return SkipTemplate


def FallbackCard(row_num, fallback_message):
    fallback_card_name = f"Row{row_num}Fallback"

    FallbackTemplate = f'''
card {fallback_card_name} when fallback == True, then: Row{row_num} do
  fallback = False
  
  text("""
  {fallback_message}
  """)
end
    '''
    return FallbackTemplate


def InputFallbackCard(
    row_num,
    go_to,
    target_input,
    affirmation=DEFAULT_AFFIRMATION,
    fallback_message=DEFAULT_FALLBACK_MESSAGE,
):
    # fallback_card_name = f'Row{row_num}Fallback'

    FallbackTemplate = f'''
card Row{row_num}Manager when response == {target_input}, then: Row{go_to} do
  text("{affirmation}")
end

card Row{row_num}Manager when fallback != {target_input}, then: Row{row_num} do
  text("""
  {fallback_message}
  """)
end
    '''
    return FallbackTemplate


def select_text_card_format(row_data):
    card = ""
    if row_data["go_to_row_num"]:
        card = ContinuingCard(
            row_data["row_num"],
            row_data["go_to_row_num"],
            row_data["bot_content"],
            row_data["link"],
        )
    elif row_data["go_to"] == 0 or row_data["go_to"] is False:
        card = TerminalCard(
            row_data["row_num"], row_data["bot_content"], row_data["link"]
        )
    return card


def ListMenuCard(row_num, options, text, title, header, footer, fallback=None):
    menu_text = f'text("{text}")'
    menu_header = f'header("{header}")'
    menu_footer = f'footer("{footer}")'
    menu_options = ", ".join(options)

    menuCardTemplate = f"""
card Row{row_num} do
  list("{title}", [{menu_options}]) do
    {menu_text}
    {menu_header}
    {menu_footer}
  end
end
"""
    return menuCardTemplate


def MenuOptionCard(row_num, button_text, go_to_row_num, link=None):
    content = 'log("Placeholder")'
    if link:
        content = f'run_stack("{link}")'

    go_to = f", then: Row{go_to_row_num}"
    if go_to_row_num == 0:
        go_to = ""

    buttonOptionTemplate = f"""
card Row{row_num}, "{button_text}" {go_to} do
  {content}
  
end
    """
    return buttonOptionTemplate


def select_image_card_format(script_df, index, row_data, skip_row=None):
    """FIXME: skip_row is unused"""
    i = row_data["row_num"]
    row_check = True
    while row_check:
        i += 1
        if (script_df.iloc[[i - 2]]["Type"] == "Text").bool():
            text = script_df.loc[i - 1]["Bot says"]
            go_to = script_df.loc[i - 1]["Go to Row Number"]

            card = ImageCardWithText(row_data["row_num"], go_to, row_data["link"], text)
            row_check = False
            # skip_row = index+1
        elif (script_df.iloc[[i - 2]]["Type"] == "Button Prompt").bool():
            buttons_arr = []
            j = i
            button_check = True
            text = script_df.loc[j - 1]["Bot says"]
            fallback = script_df.loc[j - 1]["Fallback Strategy"]
            # skip_row = index + 1

            while button_check:
                j += 1
                # iloc uses a different value than i (-2) because it has header row and Row 2
                if (script_df.iloc[[j - 2]]["Type"] == "Button").bool():
                    buttons_arr.append(f"Row{j}")
                else:
                    button_check = False

            if fallback == 0:
                fallback = None

            card = ImageCardWithButtons(
                row_data["row_num"],
                buttons_arr,
                row_data["link"],
                text,
                script_df.iloc[0]["Reprompt Fallback Message"],
            )

            if fallback == "Reprompt":
                card += FallbackCard(
                    row_data["row_num"], script_df.iloc[0]["Reprompt Fallback Message"]
                )

            row_check = False

    return card


def select_button_prompt_card_format(script_df, row_data):
    buttons_arr = []
    i = row_data["row_num"]
    row_check = True

    while row_check:
        i += 1
        if (script_df.iloc[[i - 2]]["Type"] == "Button").bool():
            buttons_arr.append(f"Row{i}")
        else:
            row_check = False

    if not row_data["fallback"]:
        row_data["fallback"] = None

    if row_data["variable_action"] == 0:
        row_data["variable_action"] = None

    card = ButtonCard(
        row_data["row_num"],
        buttons_arr,
        row_data["bot_content"],
        row_data["fallback"],
        row_data["variable_name"],
        row_data["variable_action"],
    )

    if row_data["fallback"] == "Reprompt":
        card += FallbackCard(row_data["row_num"], row_data["fallback"])

    return card


def select_button_card_format(row_data):
    if row_data["variable_name"] == 0:
        row_data["variable_name"] = None

    if row_data["variable_action"] == 0:
        row_data["variable_action"] = None

    card = ButtonOption(
        row_data["row_num"],
        row_data["user_content"],
        row_data["go_to_row_num"],
        row_data["link"],
        row_data["variable_name"],
        row_data["variable_action"],
    )

    return card


def select_user_input_card_format(script_df, row_data):
    go_to = script_df.loc[row_data["row_num"] + 1 - 2]["Go to Row Number"]

    next_row_type = script_df.loc[row_data["row_num"] + 2 - 2]["Type"]
    next_row_type = next_row_type.lower().strip()

    if next_row_type == "skip":
        skip_trigger = script_df.loc[row_data["row_num"] + 2 - 2]["User says"]

        skip_row_num = row_data["row_num"] + 2
        skip_go_to_row = row_data["row_num"] + 3
        go_to = skip_row_num

    fallback = None
    if row_data["fallback"]:
        fallback = row_data["fallback"].lower().strip()

    if row_data["variable_name"]:
        card = SavedInputCard(
            row_data["row_num"],
            go_to,
            row_data["bot_content"],
            row_data["variable_name"],
        )
    else:
        card = UnsavedInputCard(
            row_data["row_num"], go_to, row_data["bot_content"], fallback
        )

    if fallback == "Reprompt":
        target_input = script_df.loc[row_data["row_num"] - 1]["User says"]
        card += InputFallbackCard(
            row_data["row_num"],
            go_to,
            target_input,
            "Good",  # affirmation
            "Try again",  # fallback_message
        )

    if next_row_type == "skip":
        card += SkipCard(
            skip_row_num, skip_go_to_row, skip_trigger, row_data["variable_name"]
        )
    return card


def select_list_menu_card_format(script_df, row_data):
    # menu_text = row_data['bot_content']
    menu_header = ""
    menu_footer = ""
    menu_title = ""

    menu_options = []

    menu_option_cards = []

    i = row_data["row_num"]
    row_check = True

    while row_check:
        i += 1
        try:
            row_type = script_df.iloc[[i - 2]]["Type"]
        except:
            break
        if (row_type == "List Menu Title").bool():
            menu_title = script_df.loc[i - 1]["Bot says"]
        elif (row_type == "List Menu Header").bool():
            menu_header = script_df.loc[i - 1]["Bot says"]
        elif (row_type == "List Menu Footer").bool():
            menu_footer = script_df.loc[i - 1]["Bot says"]
        elif (row_type == "List Menu Option").bool():
            menu_options.append(f"Row{i}")
            menu_option_card = MenuOptionCard(
                i,
                script_df.loc[i - 1]["User says"],
                script_df.loc[i - 1]["Go to Row Number"],
                script_df.loc[i - 1]["Links"],
            )
            menu_option_cards.append(menu_option_card)
        else:
            row_check = False

    card = ListMenuCard(
        row_data["row_num"],
        menu_options,
        row_data["bot_content"],
        menu_title,
        menu_header,
        menu_footer,
    )

    for menu_card in menu_option_cards:
        card += menu_card

    return card


def evaluate_card_type_of_row(script_df, index, row_data):
    if row_data["user_content"] == "<user input>":
        card = ""
    elif row_data["row_type"] == "Text":
        card = select_text_card_format(row_data)
    elif row_data["row_type"] == "Image":
        card = select_image_card_format(script_df, index, row_data)
    elif row_data["row_type"] == "Button Prompt":
        card = select_button_prompt_card_format(script_df, row_data)
    elif row_data["row_type"] == "Button":
        card = select_button_card_format(row_data)
    elif row_data["row_type"] == "User Input Prompt":
        card = select_user_input_card_format(script_df, row_data)
    elif row_data["row_type"] == "List Menu":
        card = select_list_menu_card_format(script_df, row_data)
    else:
        card = ""

    return card


def get_values_for_a_row(index, row):
    row_data = {
        "row_num": index + 2,  # Index
        "row_type": row["Type"],  # Column A
        "bot_content": row["Bot says"],  # Column B
        "user_content": row["User says"],  # Column C
        "go_to": row["Go to"],  # Column D
        "go_to_row_num": row["Go to Row Number"],  # Column E
        "variable_name": row["Save to Variable"],  # Column F
        "variable_action": row["Variable Action"],  # Column G
        "fallback": row["Fallback Strategy"],  # Column H
        "link": row["Links"],  # Column I
        "notes": row["Notes/comments"],  # Column J
        "card_title": f"Row{index+2}",
    }
    return row_data


def loop_through_df_rows(script_df):
    card_arr = []
    for index, row in script_df.iterrows():
        row_data = get_values_for_a_row(index, row)
        card = evaluate_card_type_of_row(script_df, index, row_data)
        card_arr.append(card.strip())
    return card_arr


def write_file(card_arr):
    with open("refactoring_test.txt", "w", encoding="utf-8") as f:
        for card in card_arr:
            if not card.strip():
                continue
            f.write(card.strip() + "\n")
    return "\n".join(card_arr)


def convert(path=DEFAULT_XLSX_PATH):
    """Convert xlsx file to Turn.IO Stacks text file"""
    script_df = read_and_preprocess_spreadsheet(DEFAULT_XLSX_PATH)
    card_arr = loop_through_df_rows(script_df)
    return write_file(card_arr)


if __name__ == "__main__":
    path = DEFAULT_XLSX_PATH
    if len(sys.argv) > 1:
        path = Path(sys.arg[1])
    print(convert(path=path))
