#frozen_string_literal: true
module DiscourseAi
  module AiBot
    module Commands
      class Parameter
        attr_reader :name, :description, :type, :enum, :required
        def initialize(name:, description:, type:, enum: nil, required: false)
          @name = name
          @description = description
          @type = type
          @enum = enum
          @required = required
        end
      end
      class Command
        CARET = ""
        PROGRESS_CARET = ""
        class << self
          def name
            raise NotImplemented
          end
          def invoked?(cmd_name)
            cmd_name == name
          end
          def desc
            raise NotImplemented
          end
          def custom_system_message
          end
          def parameters
            raise NotImplemented
          end
        end
        attr_reader :bot_user
        def initialize(bot_user:, args:, post: nil, parent_post: nil)
          @bot_user = bot_user
          @args = args
          @post = post
          @parent_post = parent_post
          @placeholder = +(<<~HTML).strip
            
              #{I18n.t("discourse_ai.ai_bot.command_summary.#{self.class.name}")}
              
                #{CARET}
              
             
            #{PROGRESS_CARET}
          HTML
          @invoked = false
        end
        def bot
          @bot ||= DiscourseAi::AiBot::Bot.as(bot_user)
        end
        def tokenizer
          bot.tokenizer
        end
        def standalone?
          false
        end
        def low_cost?
          false
        end
        def result_name
          raise NotImplemented
        end
        def name
          raise NotImplemented
        end
        def process(post)
          raise NotImplemented
        end
        def description_args
          {}
        end
        def custom_raw
        end
        def chain_next_response
          true
        end
        def show_progress(text, progress_caret: false)
          # during tests we may have none
          caret = progress_caret ? PROGRESS_CARET : CARET
          new_placeholder = @placeholder.sub(caret, text + caret)
          raw = @post.raw.sub(@placeholder, new_placeholder)
          @placeholder = new_placeholder
          @post.revise(bot_user, { raw: raw }, skip_validations: true, skip_revision: true)
        end
        def localized_description
          I18n.t(
            "discourse_ai.ai_bot.command_description.#{self.class.name}",
            self.description_args,
          )
        end
        def invoke!
          raise StandardError.new("Command can only be invoked once!") if @invoked
          @invoked = true
          if !@post
            @post =
              PostCreator.create!(
                bot_user,
                raw: @placeholder,
                topic_id: @parent_post.topic_id,
                skip_validations: true,
                skip_rate_limiter: true,
              )
          else
            @post.revise(
              bot_user,
              { raw: @post.raw + "\n\n" + @placeholder + "\n\n" },
              skip_validations: true,
              skip_revision: true,
            )
          end
          @post.post_custom_prompt ||= @post.build_post_custom_prompt(custom_prompt: [])
          prompt = @post.post_custom_prompt.custom_prompt || []
          parsed_args = JSON.parse(@args).symbolize_keys
          prompt << [process(**parsed_args).to_json, self.class.name, "function"]
          @post.post_custom_prompt.update!(custom_prompt: prompt)
          raw = +(<<~HTML)
          
            #{I18n.t("discourse_ai.ai_bot.command_summary.#{self.class.name}")}
            
              #{localized_description}
            
           
          HTML
          raw << custom_raw if custom_raw.present?
          raw = @post.raw.sub(@placeholder, raw)
          @post.revise(bot_user, { raw: raw }, skip_validations: true, skip_revision: true)
          if chain_next_response
            # somewhat annoying but whitespace was stripped in revise
            # so we need to save again
            @post.raw = raw
            @post.save!(validate: false)
          end
          [chain_next_response, @post]
        end
        def format_results(rows, column_names = nil, args: nil)
          rows = rows&.map { |row| yield row } if block_given?
          if !column_names
            index = -1
            column_indexes = {}
            rows =
              rows&.map do |data|
                new_row = []
                data.each do |key, value|
                  found_index = column_indexes[key.to_s] ||= (index += 1)
                  new_row[found_index] = value
                end
                new_row
              end
            column_names = column_indexes.keys
          end
          # this is not the most efficient format
          # however this is needed cause GPT 3.5 / 4 was steered using JSON
          result = { column_names: column_names, rows: rows }
          result[:args] = args if args
          result
        end
        protected
        attr_reader :bot_user, :args
      end
    end
  end
end