import openai from '../external-services/openai.js';

const getMajorScaleBiases = (root: number) => {
  const coreNotes = [0, 4, 7].map((n) => (n + root) % 12);
  const secondaryNotes = [2, 5, 9, 11].map((n) => (n + root) % 12);
  return {
    [`pitch % 12 != ${root}`]: -1,
    [`!(pitch % 12 in [${coreNotes.join(', ')}])`]: -2,
    [`!(pitch % 12 in [${[...coreNotes, ...secondaryNotes].join(', ')}])`]: -3,
  };
};

const getMinorScaleBiases = (root: number) => {
  const coreNotes = [0, 3, 7].map((n) => (n + root) % 12);
  const secondaryNotes = [2, 5, 9, 10].map((n) => (n + root) % 12);
  return {
    [`pitch % 12 != ${root}`]: -1,
    [`!(pitch % 12 in [${coreNotes.join(', ')}])`]: -2,
    [`!(pitch % 12 in [${[...coreNotes, ...secondaryNotes].join(', ')}])`]: -3,
  };
};

const keyBiases = {
  'C+': getMajorScaleBiases(0),
  'C#+': getMajorScaleBiases(1),
  'D+': getMajorScaleBiases(2),
  'D#+': getMajorScaleBiases(3),
  'E+': getMajorScaleBiases(4),
  'F+': getMajorScaleBiases(5),
  'F#+': getMajorScaleBiases(6),
  'G+': getMajorScaleBiases(7),
  'G#+': getMajorScaleBiases(8),
  'A+': getMajorScaleBiases(9),
  'A#+': getMajorScaleBiases(10),
  'B+': getMajorScaleBiases(11),

  'C-': getMinorScaleBiases(0),
  'C#-': getMinorScaleBiases(1),
  'D-': getMinorScaleBiases(2),
  'D#-': getMinorScaleBiases(3),
  'E-': getMinorScaleBiases(4),
  'F-': getMinorScaleBiases(5),
  'F#-': getMinorScaleBiases(6),
  'G-': getMinorScaleBiases(7),
  'G#-': getMinorScaleBiases(8),
  'A-': getMinorScaleBiases(9),
  'A#-': getMinorScaleBiases(10),
  'B-': getMinorScaleBiases(11),
};

const interpretComposerPrompt = async (prompt: string) => {
  try {
    const response = await openai.chat.completions.create({
      model: 'gpt-4o',
      temperature: 0.8,
      tool_choice: {
        type: 'function',
        function: {
          name: 'composeNotes',
        },
      },
      tools: [
        {
          type: 'function',
          function: {
            name: 'composeNotes',
            parameters: {
              type: 'object',
              properties: {
                key: {
                  type: 'string',
                  description:
                    'The key of the music to generate. Populate this if and only if the user specifies a key.',
                  enum: Object.keys(keyBiases),
                },
                additionalTags: {
                  type: 'string',
                  description:
                    'Space-separated tags describing the style of music that would include the content the user is asking to generate. Be thorough - include details about genre, time period, and the names of five related artists (not including any specified by the user, or any members of any band or other collective specified by the user). Do not enter timing or key information here - these should be entered in the "noteLengthMin", "noteLengthMax", and "key" fields.',
                },
                noteLengthMin: {
                  type: 'number',
                  description:
                    'The minimum length of each note in the generated music, in beats. Use this and "noteLengthMax" to affect the speed of the output, or to match the timing to the user\'s request. Eighth Note = 0.5. Quarter Note = 1. Sixteenth Note = 0.25. Eighth note triplets = 0.33. Sixteenth note triplets = 0.17. Quarter note triplets = 0.67. If a user says "triplets" they probably mean eighth note triplets.',
                },
                noteLengthMax: {
                  type: 'number',
                  description: 'The maximum length of each note in the generated music, in beats.',
                },
              },
              required: ['additionalTags'],
            },
          },
        },
      ],
      messages: [
        {
          role: 'system',
          content:
            'You are choosing the correct parameters to pass to a music composition API. The user will describe what they want, and you will invoke the "composeNotes" function with the correct parameters.',
        },
        {
          role: 'user',
          content: `${prompt}`,
        },
      ],
    });

    const { additionalTags, key, noteLengthMin, noteLengthMax } = JSON.parse(
      response.choices[0].message.tool_calls[0].function.arguments
    );

    let newBias = {};
    if (key) {
      newBias = {
        ...newBias,
        ...keyBiases[key],
      };
    }
    if (noteLengthMax && (!noteLengthMin || noteLengthMax >= noteLengthMin)) {
      newBias = {
        ...newBias,
        [`length > ${noteLengthMax + 0.025}`]: -200,
      };
    }
    if (noteLengthMin && (!noteLengthMax || noteLengthMin <= noteLengthMax)) {
      newBias = {
        ...newBias,
        [`rest && length < ${noteLengthMin - 0.025}`]: -200,
      };
    }

    return {
      textPrompt: additionalTags,
      bias: newBias,
    };
  } catch (e) {
    return { textPrompt: '', bias: {} };
  }
};

export default interpretComposerPrompt;
