import base64
import dill
import os
import json
import jsonpickle
import pickle
import random
import requests

from dotenv import load_dotenv
from mathtext_fastapi.nlu import evaluate_message_with_nlu
from mathtext_fastapi.math_quiz_fsm import MathQuizFSM
from mathtext_fastapi.math_subtraction_fsm import MathSubtractionFSM
from supabase import create_client
from transitions import Machine

from mathactive.generators import start_interactive_math
from mathactive.hints import generate_hint

load_dotenv()

SUPA = create_client(
    os.environ.get('SUPABASE_URL'),
    os.environ.get('SUPABASE_KEY')
)


def create_text_message(message_text, whatsapp_id):
    """ Fills a template with input values to send a text message to Whatsapp

    Inputs
    - message_text: str - the content that the message should display
    - whatsapp_id: str - the message recipient's phone number

    Outputs
    - message_data: dict - a preformatted template filled with inputs
    """
    message_data = {
        "preview_url": False,
        "recipient_type": "individual",
        "to": whatsapp_id,
        "type": "text",
        "text": {
            "body": message_text
        }
    }
    return message_data


def create_button_objects(button_options):
    """ Creates a list of button objects using the input values
    Input
    - button_options: list - a list of text to be displayed in buttons

    Output
    - button_arr: list - preformatted button objects filled with the inputs

    NOTE: Not fully implemented and tested
    """
    button_arr = []
    for option in button_options:
        button_choice = {
            "type": "reply",
            "reply": {
                "id": "inquiry-yes",
                "title": option['text']
            }
        }
        button_arr.append(button_choice)
    return button_arr


def create_interactive_message(message_text, button_options, whatsapp_id):
    """ Fills a template to create a button message for Whatsapp

    * NOTE: Not fully implemented and tested
    * NOTE/TODO: It is possible to create other kinds of messages
                 with the 'interactive message' template
    * Documentation:
      https://whatsapp.turn.io/docs/api/messages#interactive-messages

    Inputs
    - message_text: str - the content that the message should display
    - button_options: list - what each button option should display
    - whatsapp_id: str - the message recipient's phone number
    """
    button_arr = create_button_objects(button_options)

    data = {
        "to": whatsapp_id,
        "type": "interactive",
        "interactive": {
            "type": "button",
            # "header": { },
            "body": {
                "text": message_text
            },
            # "footer": { },
            "action": {
                "buttons": button_arr
            }
        }
    }
    return data


def pickle_and_encode_state_machine(state_machine):
    dump = pickle.dumps(state_machine)
    dump_encoded = base64.b64encode(dump).decode('utf-8')
    return dump_encoded


def manage_math_quiz_fsm(user_message, contact_uuid, type):
    fsm_check = SUPA.table('state_machines').select("*").eq(
        "contact_uuid",
        contact_uuid
    ).execute()

    # This doesn't allow for when one FSM is present and the other is empty
    """
    1
    data=[] count=None
    
    2
    data=[{'id': 29, 'contact_uuid': 'j43hk26-2hjl-43jk-hnk2-k4ljl46j0ds09', 'addition3': None, 'subtraction': None, 'addition': 

    - but problem is there is no subtraction , but it's assuming there's a subtration


    Cases
    - make a completely new record
    - update an existing record with an existing FSM
    - update an existing record without an existing FSM
    """


    # Make a completely new entry
    if fsm_check.data == []:
        if type == 'addition':
            math_quiz_state_machine = MathQuizFSM()
        else:
            math_quiz_state_machine = MathSubtractionFSM()
        messages = [math_quiz_state_machine.response_text]
        dump_encoded = pickle_and_encode_state_machine(math_quiz_state_machine)

        SUPA.table('state_machines').insert({
            'contact_uuid': contact_uuid,
            f'{type}': dump_encoded
        }).execute()
    # Update an existing record with a new state machine
    elif not fsm_check.data[0][type]:
        if type == 'addition':
            math_quiz_state_machine = MathQuizFSM()
        else:
            math_quiz_state_machine = MathSubtractionFSM()
        messages = [math_quiz_state_machine.response_text]
        dump_encoded = pickle_and_encode_state_machine(math_quiz_state_machine)

        SUPA.table('state_machines').update({
            f'{type}': dump_encoded
        }).eq(
            "contact_uuid", contact_uuid
        ).execute()      
    # Update an existing record with an existing state machine
    elif fsm_check.data[0][type]:
        undump_encoded = base64.b64decode(
            fsm_check.data[0][type].encode('utf-8')
        )
        math_quiz_state_machine = pickle.loads(undump_encoded)

        math_quiz_state_machine.student_answer = user_message
        math_quiz_state_machine.correct_answer = str(math_quiz_state_machine.correct_answer)
        messages = math_quiz_state_machine.validate_answer()
        dump_encoded = pickle_and_encode_state_machine(math_quiz_state_machine)          
        SUPA.table('state_machines').update({
            f'{type}': dump_encoded
        }).eq(
            "contact_uuid", contact_uuid
        ).execute()
    return messages


def use_quiz_module_approach(user_message, context_data):
    print("USER MESSAGE")
    print(user_message)
    print("=======================")
    if user_message == 'add':
        context_result = start_interactive_math()
        message_package = {
            'messages': [
                "Great, let's do some addition",
                "First, we'll start with single digits.",
                "Type your response as a number.  For example, for '1 + 1', you'd write 2."
            ],
            'input_prompt': context_result['text'],
            'state': "addition-question-sequence"
        }

    elif user_message == context_data.get('right_answer'):
        context_result = start_interactive_math(
            context_data['number_correct'],
            context_data['number_incorrect'],
            context_data['level']
        )
        message_package = {
            'messages': [
                "That's right, great!",
            ],
            'input_prompt': context_result['text'],
            'state': "addition-question-sequence"
        }
    else:
        context_result = generate_hint(
            context_data['question_numbers'],
            context_data['right_answer'],
            context_data['number_correct'],
            context_data['number_incorrect'],
            context_data['level'],
            context_data['hints_used']
        )
        message_package = {
            'messages': [
                context_result['text'],
            ],
            'input_prompt': context_data['text'],
            'state': "addition-question-sequence"
        }
    return message_package, context_result


def return_next_conversational_state(context_data, user_message, contact_uuid):
    """ Evaluates the conversation's current state to determine the next state

    Input
    - context_data: dict - data about the conversation's current state
    - user_message: str - the message the user sent in response to the state

    Output
    - message_package: dict - a series of messages and prompt to send
    """
    if context_data['user_message'] == '' and \
       context_data['state'] == 'start-conversation':
        message_package = {
            'messages': [],
            'input_prompt': "Welcome to our math practice.  What would you like to try?  Type add or subtract.",
            'state': "welcome-sequence"
        }
    elif context_data['state'] == 'addition-question-sequence' or \
        user_message == 'add':

        # Used in FSM
        # messages = manage_math_quiz_fsm(user_message, contact_uuid)

        # message_package, context_result = use_quiz_module_approach(user_message, context_data)
        messages = manage_math_quiz_fsm(user_message, contact_uuid, 'addition')

        if user_message == 'exit':
            state_label = 'exit'
        else:
            state_label = 'addition-question-sequence'
        # Used in FSM
        input_prompt = messages.pop()
        message_package = {
            'messages': messages,
            'input_prompt': input_prompt,
            'state': state_label
        }

        # Used in quiz w/ hints
        # context_data = context_result
        # message_package['state'] = state_label

    elif context_data['state'] == 'subtraction-question-sequence' or \
        user_message == 'subtract':
        messages = manage_math_quiz_fsm(user_message, contact_uuid, 'subtraction')

        if user_message == 'exit':
            state_label = 'exit'
        else:
            state_label = 'subtraction-question-sequence'

        input_prompt = messages.pop()

        message_package = {
            'messages': messages,
            'input_prompt': input_prompt,
            'state': state_label
        }

        # message_package = {
        #     'messages': [
        #         "Time for some subtraction!",
        #         "Type your response as a number.  For example, for '1 - 1', you'd write 0."
        #     ],
        #     'input_prompt': "Here's the first one... What's 3-1?",
        #     'state': "subtract-question-sequence"
        # }
    elif context_data['state'] == 'exit' or user_message == 'exit':
        message_package = {
            'messages': [
                "Great, thanks for practicing math today.  Come back any time."
            ],
            'input_prompt': "",
            'state': "exit"
        }
    else:
        message_package = {
            'messages': [
                "Hmmm...sorry friend.  I'm not really sure what to do."
            ],
            'input_prompt': "Please type add or subtract to start a math activity.",
            'state': "reprompt-menu-options"
        }
    # Used in FSM
    return message_package

    # Used in quiz folder approach
    # return context_result, message_package


def manage_conversation_response(data_json):
    """ Calls functions necessary to determine message and context data to send

    Input
    - data_json: dict - message data from Turn.io/Whatsapp

    Output
    - context: dict - a record of the state at a given point a conversation

    TODOs
    - implement logging of message
    - test interactive messages
    - review context object and re-work to use a standardized format
    - review ways for more robust error handling
    - need to make util functions that apply to both /nlu and /conversation_manager
    """
    message_data = data_json.get('message_data', '')
    context_data = data_json.get('context_data', '')

    whatsapp_id = message_data['author_id']
    user_message = message_data['message_body']
    contact_uuid = message_data['contact_uuid']

    # TODO: Need to incorporate nlu_response into wormhole by checking answers against database (spreadsheet?)
    nlu_response = evaluate_message_with_nlu(message_data)

    if context_data['state'] == 'addition':
        context_result, message_package = return_next_conversational_state(
            context_data,
            user_message,
            contact_uuid
        )
    else:
        message_package = return_next_conversational_state(
            context_data,
            user_message,
            contact_uuid
        )

    headers = {
        'Authorization': f"Bearer {os.environ.get('TURN_AUTHENTICATION_TOKEN')}",
        'Content-Type': 'application/json'
    }

    # Send all messages for the current state before a user input prompt (text/button input request)
    for message in message_package['messages']:
        data = create_text_message(message, whatsapp_id)

        print("data")
        print(data)

        r = requests.post(
            f'https://whatsapp.turn.io/v1/messages',
            data=json.dumps(data),
            headers=headers
        )

    # Update the context object with the new state of the conversation
    if context_data['state'] == 'addition':
        context = {
            "context": {
                "user": whatsapp_id,
                "state": message_package['state'],
                "bot_message": message_package['input_prompt'],
                "user_message": user_message,
                "type": 'ask',
                # Necessary for quiz folder approach
                "text": context_result.get('text'),
                "question_numbers": context_result.get('question_numbers'),
                "right_answer": context_result.get('right_answer'),
                "number_correct": context_result.get('number_correct'),
                "hints_used": context_result.get('hints_used'),
            }
        }
    else:
        context = {
            "context": {
                "user": whatsapp_id,
                "state": message_package['state'],
                "bot_message": message_package['input_prompt'],
                "user_message": user_message,
                "type": 'ask',
            }
        }

    return context

    # data = {
    #     "to": whatsapp_id,
    #     "type": "interactive",
    #     "interactive": {
    #         "type": "button",
    #         # "header": { },
    #         "body": {
    #             "text": "Did I answer your question?"
    #         },
    #         # "footer": { },
    #         "action": {
    #             "buttons": [
    #                 {
    #                     "type": "reply",
    #                     "reply": {
    #                         "id": "inquiry-yes",
    #                         "title": "Yes"
    #                     }
    #                 },
    #                 {
    #                     "type": "reply",
    #                     "reply": {
    #                         "id": "inquiry-no",
    #                         "title": "No"
    #                     }
    #                 }
    #             ]
    #         }
    #     }
    # }