#frozen_string_literal: true

module DiscourseAi
  module AiBot
    module Personas
      def self.all(user: nil)
        personas = [Personas::General, Personas::SqlHelper]
        personas << Personas::Artist if SiteSetting.ai_stability_api_key.present?
        personas << Personas::SettingsExplorer
        personas << Personas::Researcher if SiteSetting.ai_google_custom_search_api_key.present?
        personas << Personas::Creative

        personas_allowed = SiteSetting.ai_bot_enabled_personas.split("|")
        personas =
          personas.filter do |persona|
            personas_allowed.include?(persona.to_s.demodulize.underscore)
          end

        if user
          personas.concat(
            AiPersona.all_personas.filter do |persona|
              user.in_any_groups?(persona.allowed_group_ids)
            end,
          )
        end

        personas
      end

      class Persona
        def self.name
          I18n.t("discourse_ai.ai_bot.personas.#{to_s.demodulize.underscore}.name")
        end

        def self.description
          I18n.t("discourse_ai.ai_bot.personas.#{to_s.demodulize.underscore}.description")
        end

        def initialize(allow_commands: true)
          @allow_commands = allow_commands
        end

        def commands
          []
        end

        def render_commands(render_function_instructions:)
          return +"" if !@allow_commands

          result = +""
          if render_function_instructions
            result << "\n"
            result << function_list.system_prompt
            result << "\n"
          end
          result << available_commands.map(&:custom_system_message).compact.join("\n")
          result
        end

        def render_system_prompt(topic: nil, render_function_instructions: true)
          substitutions = {
            site_url: Discourse.base_url,
            site_title: SiteSetting.title,
            site_description: SiteSetting.site_description,
            time: Time.zone.now,
            commands: render_commands(render_function_instructions: render_function_instructions),
          }

          substitutions[:participants] = topic.allowed_users.map(&:username).join(", ") if topic

          system_prompt.gsub(/\{(\w+)\}/) do |match|
            found = substitutions[match[1..-2].to_sym]
            found.nil? ? match : found.to_s
          end
        end

        def available_commands
          return [] if !@allow_commands

          return @available_commands if @available_commands

          @available_commands = all_available_commands.filter { |cmd| commands.include?(cmd) }
        end

        def available_functions
          return [] if !@allow_commands
          # note if defined? can be a problem in test
          # this can never be nil so it is safe
          return @available_functions if @available_functions

          functions = []

          functions =
            available_commands.map do |command|
              function =
                DiscourseAi::Inference::Function.new(name: command.name, description: command.desc)
              command.parameters.each { |parameter| function.add_parameter(parameter) }
              function
            end

          @available_functions = functions
        end

        def function_list
          return @function_list if @function_list

          @function_list = DiscourseAi::Inference::FunctionList.new
          available_functions.each { |function| @function_list << function }
          @function_list
        end

        def all_available_commands
          return @cmds if @cmds

          all_commands = [
            Commands::CategoriesCommand,
            Commands::TimeCommand,
            Commands::SearchCommand,
            Commands::SummarizeCommand,
            Commands::ReadCommand,
          ]

          all_commands << Commands::TagsCommand if SiteSetting.tagging_enabled
          all_commands << Commands::ImageCommand if SiteSetting.ai_stability_api_key.present?
          if SiteSetting.ai_google_custom_search_api_key.present? &&
               SiteSetting.ai_google_custom_search_cx.present?
            all_commands << Commands::GoogleCommand
          end

          allowed_commands = SiteSetting.ai_bot_enabled_chat_commands.split("|")
          @cmds = all_commands.filter { |klass| allowed_commands.include?(klass.name) }
        end
      end
    end
  end
end
