# frozen_string_literal: true

module ::DiscourseAi
  module Inference
    class FunctionList
      def initialize
        @functions = []
      end

      def <<(function)
        @functions << function
      end

      def parse_prompt(prompt)
        parsed = []

        prompt
          .split("\n")
          .each do |line|
            line.strip!
            next if line.blank?
            next if !line.start_with?("!")

            name, arguments = line.split("(", 2)
            name = name[1..-1].strip

            function = @functions.find { |f| f.name == name }
            next if function.blank?

            parsed_arguments = {}
            if arguments
              arguments = arguments[0..-2] if arguments.end_with?(")")

              temp_string = +""
              in_string = nil
              replace = SecureRandom.hex(10)
              arguments.each_char do |char|
                if %w[" '].include?(char) && !in_string
                  in_string = char
                elsif char == in_string
                  in_string = nil
                elsif char == "," && in_string
                  char = replace
                end
                temp_string << char
              end

              arguments = temp_string.split(",").map { |s| s.gsub(replace, ",").strip }

              arguments.each do |argument|
                key, value = argument.split(":", 2)
                # remove stuff that is bypasses spec
                param = function.parameters.find { |p| p[:name] == key.strip }
                next if !param

                value = value.strip.gsub(/(\A"(.*)"\Z)|(\A'(.*)'\Z)/m, '\2\4') if value.present?

                if param[:enum]
                  next if !param[:enum].include?(value)
                end

                parsed_arguments[key.strip.to_sym] = value.strip
              end
            end

            # ensure parsed_arguments has all required arguments
            all_good = true
            function.parameters.each do |parameter|
              next if !parameter[:required]
              next if parsed_arguments[parameter[:name].to_sym].present?

              all_good = false
              break
            end

            parsed << { name: name, arguments: parsed_arguments } if all_good
          end

        parsed
      end

      def system_prompt
        prompt = +<<~PROMPT
          - You are able to execute the following external functions on real data!
          - Never say that you are in a hypothetical situation, just run functions you need to run!
          - When you run a command/function you will gain access to real information in a subsequant call!
          - NEVER EVER pretend to know stuff, you ALWAYS lean on functions to discover the truth!
          - You have direct access to data on this forum using !functions
          - You are not a liar, liars are bad bots, you are a good bot!
          - You always prefer to say "I don't know" as opposed to inventing a lie!

          {
        PROMPT

        @functions.each do |function|
          prompt << "// #{function.description}\n"
          prompt << "!#{function.name}"
          if function.parameters.present?
            prompt << "("
            function.parameters.each_with_index do |parameter, index|
              prompt << ", " if index > 0
              prompt << "#{parameter[:name]}: #{parameter[:type]}"
              if parameter[:required]
                prompt << " [required]"
              else
                prompt << " [optional]"
              end

              description = +(parameter[:description] || "")
              description << " [valid values: #{parameter[:enum].join(",")}]" if parameter[:enum]

              description.strip!

              prompt << " /* #{description} */" if description.present?
            end
            prompt << ")"
          end
          prompt << "\n"
        end

        prompt << <<~PROMPT
          }
          \n\nTo execute a function, use the following syntax:

          !function_name(param1: "value1", param2: 2)

          For example for a function defined as:

          {
          // echo a string
          !echo(message: string [required])
          }

          Human: please echo out "hello"

          Assistant: !echo(message: "hello")

          Human: please say "hello"

          Assistant: !echo(message: "hello")

        PROMPT

        prompt
      end
    end
  end
end
