#frozen_string_literal: true

module DiscourseAi::AiBot::Commands
  MAX_CONTEXT_TOKENS = 2000

  class SettingContextCommand < Command
    def self.rg_installed?
      if defined?(@rg_installed)
        @rg_installed
      else
        @rg_installed =
          begin
            Discourse::Utils.execute_command("which", "rg")
            true
          rescue Discourse::Utils::CommandError
            false
          end
      end
    end

    class << self
      def name
        "setting_context"
      end

      def desc
        "Will provide you with full context regarding a particular site setting in Discourse"
      end

      def parameters
        [
          Parameter.new(
            name: "setting_name",
            description: "The name of the site setting we need context for",
            type: "string",
            required: true,
          ),
        ]
      end
    end

    def result_name
      "context"
    end

    def description_args
      { setting_name: @setting_name }
    end

    CODE_FILE_EXTENSIONS = "rb,js,gjs,hbs"

    def process(setting_name:)
      if !self.class.rg_installed?
        return(
          {
            setting_name: setting_name,
            context: "This command requires the rg command line tool to be installed on the server",
          }
        )
      end

      @setting_name = setting_name
      if !SiteSetting.has_setting?(setting_name)
        { setting_name: setting_name, context: "This setting does not exist" }
      else
        description = SiteSetting.description(setting_name)
        result = +"# #{setting_name}\n#{description}\n\n"

        setting_info =
          find_setting_info(setting_name, [Rails.root.join("config", "site_settings.yml").to_s])
        if !setting_info
          setting_info =
            find_setting_info(setting_name, Dir[Rails.root.join("plugins/**/settings.yml")])
        end

        result << setting_info
        result << "\n\n"

        %w[lib app plugins].each do |dir|
          path = Rails.root.join(dir).to_s
          result << Discourse::Utils.execute_command(
            "rg",
            setting_name,
            path,
            "-g",
            "!**/spec/**",
            "-g",
            "!**/dist/**",
            "-g",
            "*.{#{CODE_FILE_EXTENSIONS}}",
            "-C",
            "10",
            "--color",
            "never",
            "--heading",
            "--no-ignore",
            chdir: path,
            success_status_codes: [0, 1],
          )
        end

        result.gsub!(/^#{Regexp.escape(Rails.root.to_s)}/, "")

        result = tokenizer.truncate(result, MAX_CONTEXT_TOKENS)

        { setting_name: setting_name, context: result }
      end
    end

    def find_setting_info(name, paths)
      path, result = nil

      paths.each do |search_path|
        result =
          Discourse::Utils.execute_command(
            "rg",
            name,
            search_path,
            "-g",
            "*.{#{CODE_FILE_EXTENSIONS}}",
            "-A",
            "10",
            "--color",
            "never",
            "--heading",
            success_status_codes: [0, 1],
          )
        if !result.blank?
          path = search_path
          break
        end
      end

      if result.blank?
        nil
      else
        rows = result.split("\n")
        leading_spaces = rows[0].match(/^\s*/)[0].length

        filtered = []

        rows.each do |row|
          if !filtered.blank?
            break if row.match(/^\s*/)[0].length <= leading_spaces
          end
          filtered << row
        end

        filtered.unshift("#{path}")
        filtered.join("\n")
      end
    end
  end
end