from openai import AsyncStream
from openai.types.chat.chat_completion_chunk import ChatCompletionChunk
from api.models import SelectedLLMProvider
from api.utils.model_utils import (
    get_large_model,
    get_llm_client,
    get_selected_llm_provider,
)
from ppt_config_generator.models import PresentationMarkdownModel
from ppt_generator.models.llm_models_with_validations import (
    LLMPresentationModelWithValidation,
)


CREATE_PRESENTATION_PROMPT = """
    You're a professional presenter with years of experience in creating clear and engaging presentations. 

    Create a presentation using the provided title, slide titles and body following specified steps and guidelines. 

    Analyze all inputs, to construct each slide with appropriate content and format.


    # Slide Types
    - **1**: contains title, description and image.
    - **2**: contains title and list of items.
    - **4**: contains title and list of items with images.
    - **5**: contains title, description and a graph.
    - **6**: contains title, description and list of items.
    - **7**: contains title and list of items with icons.
    - **8**: contains title, description and list of items with icons.

    # Steps
    1. Analyze provided presentation title, slide titles and body.
    2. Select slide type for each slide.
    3. Output should be in json format as per given schema.
    4. **Adherence to schema should be beyond all the rules mentioned in notes.**

    # Notes
    - Generate output in language mentioned in *Input*.
    - Freely select type with images and icons.
    - Introduction and Conclusion should have *Type 1* if graph is not assigned.
    - Try to select **different types for every slides**.
    - Don't select Type **3** for any slide.
    - Do not include same graph twice in presentation without any changes to the other.
    - Every series in a graph should have data in same unit. Example: all series should be in percentage or all series should be in number of items.
    - Type **9** and **5** should be only picked if graph is available.
    - **Strictly keep the text under given limit.**
    - For slide content follow these rules:
        - Highlighting in markdown format should be used to emphasize numbers and data.
        - Adhere to length contraints in **body** and **description**. Focus on direct communication within character constrainsts than lengthy explanation.
        - **body** and **description** in slides should never exceed character limits of 200 characters.
        - Specify **don't include text in image** in image prompt.
        - All the numbers should be bolded with **bold** tag in body or description of slide.
        - Image prompt should cleary define how image should look like.
        - Image prompt should not ask to generate **numbers, graphs, dashboard and report**.
        - Examples of image prompts: 
            - a travel agent presenting a detailed itinerary with photos of destinations, showcasing specific experiences, highlighting travel highlights
            - a person smiling while traveling, with a beautiful background scenery, such as mountains, beach, or city,  golden hour lighting
            - a humanoid robot standing tall, gazing confidently at the horizon, bathed in warm sunlight, the background showing a futuristic cityscape with sleek buildings and flying vehicles
        - Descriptions should be clear and to the point.
        - Descriptions should not use words like "This slide", "This presentation".
        - If **body** contains items, *choose number of items randomly between mentioned constraints.*
        - **Icon queries** must be a generic **single word noun**.
        - Provide 3 icon query for each icon where,
            - First one should be specific like "Led bulb".
            - Second one should be more generic that first like "bulb".
            - Third one should be simplest like "light".

    **Follow the all the length constraints provided in the schema and notes.**
    **Go through notes and steps and make sure they are all followed. Rule breaks are strictly not allowed.**
"""

system_prompt_with_schema = f"""
{CREATE_PRESENTATION_PROMPT}

Follow this schema while giving out response: {LLMPresentationModelWithValidation.model_json_schema()}.

Make description short and obey the character limits. Output should be in JSON format. Give out only JSON, nothing else.
"""


def get_system_prompt():
    is_google_selected = get_selected_llm_provider() == SelectedLLMProvider.GOOGLE
    return (
        system_prompt_with_schema if is_google_selected else CREATE_PRESENTATION_PROMPT
    )


def get_response_format():
    is_google_selected = get_selected_llm_provider() == SelectedLLMProvider.GOOGLE
    return (
        {
            "type": "json_object",
        }
        if is_google_selected
        else {
            "type": "json_schema",
            "json_schema": {
                "name": "LLMPresentationModel",
                "schema": LLMPresentationModelWithValidation.model_json_schema(),
            },
        }
    )


async def generate_presentation_stream(
    presentation_outline: PresentationMarkdownModel,
) -> AsyncStream[ChatCompletionChunk]:
    client = get_llm_client()
    model = get_large_model()

    response_format = get_response_format()

    response = await client.chat.completions.create(
        model=model,
        messages=[
            {
                "role": "system",
                "content": get_system_prompt(),
            },
            {
                "role": "user",
                "content": presentation_outline.to_string(),
            },
        ],
        response_format=response_format,
        stream=True,
    )

    return response


async def generate_presentation(
    presentation_outline: PresentationMarkdownModel,
) -> str:
    client = get_llm_client()
    model = get_large_model()

    response_format = get_response_format()

    response = await client.chat.completions.create(
        model=model,
        messages=[
            {
                "role": "system",
                "content": get_system_prompt(),
            },
            {
                "role": "user",
                "content": presentation_outline.to_string(),
            },
        ],
        response_format=response_format,
    )

    return response.choices[0].message.content
