<commit_before># frozen_string_literal: true

module Yuyuko
  module CommandContainer
    Event = Struct.new(:type, :key, :filter, :block)
    Command = Struct.new(:names, :attributes, :block)

    def event(type, key = nil, **filter, &block)
      raise ArgumentError, 'No callback block provided' if block.nil?
      raise ArgumentError, "Invalid event type: #{type}" unless MijDiscord::Bot::EVENTS[type]

      @event_defs ||= []
      @event_defs << Event.new(type, key, filter, block)
      nil
    end

    def command(names, **attributes, &block)
      raise ArgumentError, 'No callback block provided' if block.nil?

      names = [names] unless names.is_a?(Array)
      names = names.map {|x| Bot.parse_command_name(x) }

      attributes[:group] = @command_group

      @command_defs ||= []
      @command_defs << Command.new(names, attributes, block)
      nil
    end

    def command_group(name)
      case name
        when String then @command_group = name.to_sym
        when Symbol, NilClass then @command_group = name
        else raise ArgumentError, "Cannot use #{obj.class} as group name"
      end
    end

    def event_defs
      @event_defs
    end

    def command_defs
      @command_defs
    end
  end

  class Bot < MijDiscord::Bot
    class << self
      def parse_command_name(obj)
        case obj
          when Symbol then obj
          when Integer then obj.to_s
          when String then obj.gsub('-', '_')
          when Yuyuko::Command then obj.name
          else raise ArgumentError, "Cannot use #{obj.class} as command name"
        end.to_sym
      end
    end

    attr_reader :command_prefix

    def initialize(command_prefix:, **kwargs)
      super(**kwargs)

      @command_prefix = command_prefix

      @command_defs, @command_alias = {}, {}

      @callback_threads = []

      add_event(:create_message) do |evt|
        execute_command(evt.message)
      end
    end

    def commands
      @command_defs.values
    end

    def command(name)
      name = Bot.parse_command_name(name)
      @command_defs[@command_alias[name] || name]
    end

    def add_command(names, **attributes, &block)
      raise ArgumentError, 'No callback block provided' if block.nil?

      names = [names] unless names.is_a?(Array)
      name, aliases = names.first, names.drop(1)

      name = Bot.parse_command_name(name)
      aliases.map! {|x| Bot.parse_command_name(x) }

      aliases.each {|al| @command_alias[al] = name }
      @command_defs[name] = Yuyuko::Command.new(name, aliases, attributes, &block)
    end

    def remove_command(name)
      name = Bot.parse_command_name(name)
      aliased = @command_alias[name]

      @command_defs.delete(aliased || name)
      @command_alias.reject! {|_,v| v.name == aliased } if aliased
      nil
    end

    def include!(object)
      if object.respond_to?(:event_defs)
        object.event_defs&.each do |evt|
          add_event(evt.type, evt.key, **evt.filter, &evt.block)
        end
      end

      if object.respond_to?(:command_defs)
        object.command_defs&.each do |cmd|
          add_command(cmd.names, **cmd.attributes, &cmd.block)
        end
      end

      nil
    end

    private

    def execute_command(message)
      command_text = prefix_check?(message)
      return if command_text.nil?

      command_match = command_text.match(/\A(\S+)(?:\s+(.+))?\z/m)
      name, args = command_match[1], command_match[2] || ''

      def_name = Bot.parse_command_name(name)
      command = @command_defs[@command_alias[def_name] || def_name]

      if command.nil?
        message.channel.send_embed('core.embed.cmd_error.invalid', cmd: name)
        return
      end

      event = Yuyuko::CommandEvent.new(self, message, command)

      Thread.new do
        thread = Thread.current

        @callback_threads << thread
        thread[:mij_discord] = "yu-cmd-#{command.object_id}"<commit_msg>Hurr durr<commit_after># frozen_string_literal: true

module Yuyuko
  module CommandContainer
    Event = Struct.new(:type, :key, :filter, :block)
    Command = Struct.new(:names, :attributes, :block)

    def event(type, key = nil, **filter, &block)
      raise ArgumentError, 'No callback block provided' if block.nil?
      raise ArgumentError, "Invalid event type: #{type}" unless MijDiscord::Bot::EVENTS[type]

      @event_defs ||= []
      @event_defs << Event.new(type, key, filter, block)
      nil
    end

    def command(names, **attributes, &block)
      raise ArgumentError, 'No callback block provided' if block.nil?

      names = [names] unless names.is_a?(Array)
      names = names.map {|x| Bot.parse_command_name(x) }

      attributes[:group] = @command_group

      @command_defs ||= []
      @command_defs << Command.new(names, attributes, block)
      nil
    end

    def command_group(name)
      case name
        when String then @command_group = name.to_sym
        when Symbol, NilClass then @command_group = name
        else raise ArgumentError, "Cannot use #{obj.class} as group name"
      end
    end

    def event_defs
      @event_defs
    end

    def command_defs
      @command_defs
    end
  end

  class Bot < MijDiscord::Bot
    class << self
      def parse_command_name(obj)
        case obj
          when Symbol then obj
          when Integer then obj.to_s
          when String then obj.gsub('-', '_')
          when Yuyuko::Command then obj.name
          else raise ArgumentError, "Cannot use #{obj.class} as command name"
        end.to_sym
      end
    end

    attr_reader :command_prefix

    def initialize(command_prefix:, **kwargs)
      super(**kwargs)

      @command_prefix = command_prefix

      @command_defs, @command_alias = {}, {}

      @callback_threads = []

      add_event(:create_message) do |evt|
        execute_command(evt.message)
      end
    end

    def commands
      @command_defs.values
    end

    def command(name)
      name = Bot.parse_command_name(name)
      @command_defs[@command_alias[name] || name]
    end

    def add_command(names, **attributes, &block)
      raise ArgumentError, 'No callback block provided' if block.nil?

      names = [names] unless names.is_a?(Array)
      name, aliases = names.first, names.drop(1)

      name = Bot.parse_command_name(name)
      aliases.map! {|x| Bot.parse_command_name(x) }

      aliases.each {|al| @command_alias[al] = name }
      @command_defs[name] = Yuyuko::Command.new(name, aliases, attributes, &block)
    end

    def remove_command(name)
      name = Bot.parse_command_name(name)
      aliased = @command_alias[name]

      @command_defs.delete(aliased || name)
      @command_alias.reject! {|_,v| v.name == aliased } if aliased
      nil
    end

    def include!(object)
      if object.respond_to?(:event_defs)
        object.event_defs&.each do |evt|
          add_event(evt.type, evt.key, **evt.filter, &evt.block)
        end
      end

      if object.respond_to?(:command_defs)
        object.command_defs&.each do |cmd|
          add_command(cmd.names, **cmd.attributes, &cmd.block)
        end
      end

      nil
    end

    private

    def execute_command(message)
      command_text = prefix_check?(message)
      return if command_text.nil?

      command_match = command_text.match(/\A(\S+)(?:\s+(.+))?\z/m)
      name, args = command_match[1], command_match[2] || ''

      def_name = Bot.parse_command_name(name)
      command = @command_defs[@command_alias[def_name] || def_name]

      if command.nil?
        message.channel.send_embed('core.embed.cmd_error.invalid', cmd: name)
        return
      end

      event = Yuyuko::CommandEvent.new(self, message, command)

      Thread.new do
        thread = Thread.current

        @callback_threads << thread
        thread[:mij_discord] = "yu-cmd-#{command.name(true)}"
{'fraction_non_alphanumeric': 0.1121231558691469, 'fraction_numerical': 0.0007697241821680564, 'mean_word_length': 2.612604263206673, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7268725', 'n_tokens_mistral': 2520, 'n_tokens_neox': 2461, 'n_words': 582}