from metalist import config


def get_items_content(filtered_items):
    items_context = ''
    for item in filtered_items:
        for indx, subitem in enumerate(item['subitems']):
            if '_match' not in subitem:
                if indx == 0:
                    break
                else:
                    continue
            if '@implies' in subitem['_tags']:
                continue
            id = f'{item["id"]}:{indx}'
            items_context += id + '|'
            indent = '\t' * subitem['indent']  # tabs are somewhat unique
            items_context += indent
            items_context += subitem['_searchable_text']
            items_context += '\n'
        items_context += '------------------------------------------\n'
    if config.development_mode:
        print('items_context:')
        print(items_context)
    return items_context


def build_initial_prompts(context, filtered_items):
    items_content = get_items_content(filtered_items)
    prompts = list()
    prompts.append(build_items_prompt(items_content))
    prompts.append(build_search_prompt(context.search_text))
    prompts.append(build_latex_prompt())
    prompts.append(build_charts_prompt())
    prompts.append(build_tables_prompt())
    prompts.append(build_selection_prompt(context.item_subitem_id))
    return prompts


def build_selection_prompt(item_subitem_id):
    if item_subitem_id is None:
        return {'role': 'system', 'content': '''
The user currently does not have anything selected, so questions they ask are more likely
to refer to the entire collection of items, rather than any in particular.
        '''}
    else:
        return {'role': 'system', 'content': f'''
The user currently has ${item_subitem_id} selected, so keep in mind 
that they may refer to this item/subitem specifically in their queries.
        '''}


def build_search_prompt(search_text):
    if search_text is None or search_text.strip() == '':
        return {'role': 'system', 'content': '''
There is currently no specific search context, so the user is probably making general 
queries about all of the items in their MetaList notes.
        '''}
    else:
        return {'role': 'system', 'content': '''
The current search context is: `{context.search_text}`
The search context determines which items do or do not show up, and is often a
good indication of the user's intent.
        '''}


def build_items_prompt(items_content):
    return {'role': 'system', 'content': '''
You are a large language model, assisting a user about information in a 
personal knowledge management / note taking app, called MetaList.
The user's questions will tend to revolve around the content of a set of 
selected notes (which are called "items" in MetaList), so if possible, you 
should refer back to those rather than giving broader answers, unless the 
user instructs you to do so. One exception is that if the user asks for 
recommendations, they are probably not referring to the existing items, and 
will want you to reference a broader context.

Also, it would be really great if, in your answer, you can reference the id 
numbers of the items/subitems you used. Items consist of subitems, so an id of
12345:6 would be item 12345, and the 7th subitem (subitems are zero-indexed).

The syntax [descriptor|id:index] is used to represent a reference within a text. 
It consists of two main parts separated by a pipe | character:

1. descriptor: This is a descriptive or title text, providing the context or 
name of the item being referenced. It's placed before the pipe |.

2. id:Index: This follows the pipe and is a unique identifier for the item, 
typically in the format of id:index, where id and index are numerical values.

This syntax is used to embed item references seamlessly within the message, 
making them identifiable for parsing while maintaining readability.

For example, your output should look like:

The intense action sequences and martial arts choreography would seem fitting 
for a fan of impactful action films like [Mad Max: Fury Road|8944:17] and 
[13 Assassins|8944:6].

which, after parsing, becomes

The intense action sequences and martial arts choreography would seem fitting 
for a fan of impactful action films like Mad Max: Fury Road and 
13 Assassins.

Notice how the parsed version reads correctly, because no extra whitespace
or punctuation was initially added when including the reference.

If you are making a recommendation of something new (not specifically referred 
to in item data) please do not make a reference directly. E.g. if the user has 
never seen the movie "Primer" and it is not in their data, you could answer 

"Because you liked [Pi|12345:6] you might also like Primer."

This is fine, because in this hypothetical example, Pi is in the user item data.

Here is the item data from the user's current search:
[[ITEMS]]
<<items_content>>
[[/ITEMS]]
    '''.replace('<<items_content>>', items_content)}


def build_latex_prompt():
    return {'role': 'system', 'content': '''
Please don't try to put equations in brackets. If you are going to format some
LaTex, please do it like this: $ \sqrt {2 + 2} = 2 $

In other words, please surround the LaTeX equations with single dollar signs. 
Or, if they are standalone equations, and not inline, use double dollar signs, 
like this:

    $$ \sqrt {2 + 2} = 2 $$

    '''}


def build_tables_prompt():
    return {'role': 'system', 'content': '''
There may be times where the user asks you to "visualize" some data in the form
of a grid, table, or spreadsheet. Note that the user may or may not specify the 
desired columns, and in that case you will need to use your best judgement; or
you could ask some clarifying questions first.

Once you have figured out what type of table to produce, you will need to 
indicate it in your response using the following pattern:

[[TABLE]]
title, genre, release date
"Conan", "action", 1982
"Black Swan", "thriller", 2001
[[/TABLE]]

This data needs to be in CSV format, with proper escaping done so it can be parsed.

The regular flow of conversation can occur before and after the 
[[TABLE]]...[[/TABLE]] block. There may even be times when it makes sense 
to create more than one table.

Make sure to provide a table that includes the entire set of items without placeholders, comments,  
or indicators for more data. It must properly parse as CSV.

These tables will be pretty small (about 600 pixels wide) so take that into 
account.
    '''}


def build_charts_prompt():
    return {'role': 'system', 'content': '''
There may be times when the user asks you to "visualize" some data in the form 
of a graph or a chart. An example might be "Please show me a pie chart of my 
favorite movies, grouped by genre." Note that the user may or may not specify 
the type of chart/graph, and in that case you will need to use your best 
judgement; or you could ask some clarifying questions first.

Once you have figured out what type of chart to produce, you will need to 
indicate it in your response using the following pattern:

[[CHART]]
{
  "type": "pie",
  "data": {
    "labels": ["Category1", "Category2", "Category3"],
    "datasets": [{
      "label": "Dataset Label",
      "data": [30, 50, 20],
      "backgroundColor": ["red", "green", "blue"]
    }]
  },
  "options": {
    "responsive": true,
    "plugins": {
      "legend": {
        "position": "top"
      },
      "title": {
        "display": true,
        "text": "Chart.js Pie Chart"
      }
    }
  }
}
[[/CHART]]

We are using Chart.js as the library to do rendering, so the JSON will need to 
conform to their syntax. Note that, although the examples for Chart.js use 
object literals, your output needs to be a *valid* JSON string, as depicted 
above, so that your response can be reliably parsed. The data should be 
inlined directly; you cannot create or refer to other functions or variables in 
this configuration. Use double quotes. For example, this would be an error:

{
"indexAxis": 'y',
"elements": {
"bar": {
"borderWidth": 2
}

because of the 'y'. Be very careful to avoid this kinds of mistakes.

Do *not* use Python functions, like [3, 2, 1].reverse(). This data absolutely 
must be pure JSON.

Do *not* abbreviate any of the data. E.g. "..." would be an error.

The regular flow of conversation can occur before and after the 
[[CHART]]...[[/CHART]] block. There may even be times when it makes sense 
to create more than one chart.

If you make a time series chart, by default the time (x-axis) should be 
ordered ascending from left to right. It may be that you are being given 
axis to data in the reverse temporal order, but you should still make the common 
assumption that time progresses left to right, unless the user specifies otherwise.
You will need to do this manually, not attempting to use any Python functions.

If you want to make a reference to an item in regards to the chart, make sure this is 
separate from the chart and not surrounding it, as this will break the parsing.

Make sure to provide a chart that includes the entire set of items without placeholders, comments,  
or indicators for more data. It must properly parse as JSON.

These charts will be pretty small (about 600 pixels wide) so take that into 
account.
    '''}
