import copy
from typing import Dict, Optional, TypedDict

import openai
from hamilton.function_modifiers import config

ChatContents = TypedDict("ChatContents", {"role": str, "content": str, "type": str})


def processed_prompt(prompt: str) -> dict:
    return {"role": "user", "content": prompt, "type": "text"}


@config.when(provider="openai")
def client() -> openai.Client:
    return openai.Client()


def text_model() -> str:
    return "gpt-3.5-turbo"


def image_model() -> str:
    return "dall-e-2"


def safe(prompt: str) -> bool:
    if "unsafe" in prompt:
        return False
    return True


def modes() -> Dict[str, str]:
    return {
        "answer_question": "text",
        "generate_image": "image",
        "generate_code": "code",
        "unknown": "text",
    }


def find_mode_prompt(prompt: str, modes: Dict[str, str]) -> str:
    return (
        f"You are a chatbot. You've been prompted this: {prompt}. "
        f"You have the capability of responding in the following modes: {', '.join(modes)}. "
        "Please respond with *only* a single word representing the mode that most accurately"
        " corresponds to the prompt. Fr instance, if the prompt is 'draw a picture of a cat', "
        "the mode would be 'generate_image'. If the prompt is 'what is the capital of France', the mode would be 'answer_question'."
        "If none of these modes apply, please respond with 'unknown'."
    )


def suggested_mode(find_mode_prompt: str, client: openai.Client, text_model: str) -> str:
    result = client.chat.completions.create(
        model=text_model,
        messages=[
            {"role": "system", "content": "You are a helpful assistant"},
            {"role": "user", "content": find_mode_prompt},
        ],
    )
    content = result.choices[0].message.content
    return content


def mode(suggested_mode: str, modes: Dict[str, str]) -> str:
    # TODO -- use instructor!
    print(f"Mode: {suggested_mode}")
    lowercase = suggested_mode.lower()
    if lowercase not in modes:
        return "unknown"  # default to unknown
    return lowercase


def generated_text(chat_history: list[dict], text_model: str, client: openai.Client) -> str:
    chat_history_api_format = [
        {
            "role": chat["role"],
            "content": chat["content"],
        }
        for i, chat in enumerate(chat_history)
    ]
    result = client.chat.completions.create(
        model=text_model,
        messages=chat_history_api_format,
    )
    return result.choices[0].message.content


# def generated_text_error(generated_text) -> dict:
#     ...


def generated_image(prompt: str, image_model: str, client: openai.Client) -> str:
    result = client.images.generate(
        model=image_model, prompt=prompt, size="1024x1024", quality="standard", n=1
    )
    return result.data[0].url


def answered_question(chat_history: list[dict], text_model: str, client: openai.Client) -> str:
    chat_history = copy.deepcopy(chat_history)
    chat_history[-1][
        "content"
    ] = f"Please answer the following question: {chat_history[-1]['content']}"

    chat_history_api_format = [
        {
            "role": chat["role"],
            "content": chat["content"],
        }
        for i, chat in enumerate(chat_history)
    ]
    response = client.chat.completions.create(
        model=text_model,
        messages=chat_history_api_format,
    )
    return response.choices[0].message.content


def generated_code(chat_history: list[dict], text_model: str, client: openai.Client) -> str:
    chat_history = copy.deepcopy(chat_history)
    chat_history[-1][
        "content"
    ] = f"Please respond to the following with *only* code: {chat_history[-1]['content']}"

    chat_history_api_format = [
        {
            "role": chat["role"],
            "content": chat["content"],
        }
        for i, chat in enumerate(chat_history)
    ]
    response = client.chat.completions.create(
        model=text_model,
        messages=chat_history_api_format,
    )
    return response.choices[0].message.content


def prompt_for_more(modes: Dict[str, str]) -> str:
    return (
        f"I can't find a mode that applies to your input. Can you"
        f" please clarify? I support: {', '.join(modes)}."
    )


def processed_response(
    response: Optional[str], mode: str, modes: Dict[str, str], safe: bool
) -> ChatContents:
    if not safe:
        return {"role": "assistant", "content": "I'm sorry, I can't do that.", "type": "text"}
    return {"role": "assistant", "type": modes[mode], "content": response}


def processed_error(error: str) -> dict:
    return {"role": "assistant", "error": error, "type": "text"}
