require 'discordrb'

module Cosgrove
  require 'cosgrove/snark_commands'
  
  cattr_accessor :latest_steemit_link, :latest_golos_link
  
  @@latest_steemit_link = {}
  @@latest_golosio_link = {}
  
  class Bot < Discordrb::Commands::CommandBot
    include Support
    include SnarkCommands
    
    def initialize(options = {})
      options[:token] ||= cosgrove_token
      options[:client_id] ||= cosgrove_client_id
      super(options)
      
      @on_success_upvote_job = options[:on_success_upvote_job]
      @on_success_register_job = options[:on_success_register_job]
      
      self.bucket :voting, limit: 4, time_span: 8640, delay: 10

      add_all_commands
      add_all_messages
      SnarkCommands::add_all_snark_commands(self)
    end
    
    def add_all_messages
      # A user typed a link to steemit.com
      self.message(content: /http[s]*:\/\/steemit\.com\/.*/, ignore_bots: false) do |event|
        link = event.content.split(' ').first
        Cosgrove::latest_steemit_link[event.channel.name] = link
        append_link_details(event, link)
      end

      # A user typed a link to golos.io.
      self.message(content: /http[s]*:\/\/golos\.io\/.*/, ignore_bots: false) do |event|
        link = event.content.split(' ').first
        Cosgrove::latest_golosio_link[event.channel.name] = link
      end
    end
    
    def add_all_commands
      self.command :help do |_event, *args|
        help = []
        help << "`$slap [target]` - does a slap on the `target`"
        help << "`$verify <account> [chain]` - check `account` association with Discord users (`chain` default `steem`)"
        help << "`$register <account> [chain]` - associate `account` with your Discord user (`chain` default `steem`)"
        help << "`$upvote [url]` - upvote from #{steem_account}; empty or `^` to upvote last steemit link"
        help.join("\n")
      end
      
      self.command :version do |_|
        "cosgrove: #{Cosgrove::VERSION} :: https://github.com/steem-third-party/cosgrove"
      end
      
      self.command :verify do |event, key, chain = :steem|
        mongo_behind_warning(event)
        if key.nil?
          event.respond "To create an account: https://steemit.com/enter_email?r=#{steem_account}"
          return
        end
        
        account = find_account(key, event)
        
        if !!account && account.respond_to?(:name)
          cb_account = Cosgrove::Account.new(account.name, chain)
        else
          discord_id = key.split('@').last.split('>').first.to_i
          cb_account = Cosgrove::Account.find_by_discord_id(discord_id, chain)
          
          account = if !!cb_account
            find_account(cb_account.account_name, event)
          end
        end
        
        if !!account && !!cb_account && cb_account.discord_ids.any?
          if cb_account.hidden?
            "#{chain.to_s.upcase} account `#{account.name}` has been registered."
          else
            discord_ids = cb_account.discord_ids.map { |id| "<@#{id}>" }
            
            "#{chain.to_s.upcase} account `#{account.name}` has been registered with #{discord_ids.to_sentence}."
          end
        elsif !!account
          "#{chain.to_s.upcase} account `#{account.name}` has not been registered with any Discord account.  To register:\n`$register #{account.name}`"
        elsif discord_id.to_i > 0
          "<@#{discord_id}> has not been associated with a #{chain.to_s.upcase} account.  To register:\n`$register <account>`"
        else
          "No association found.  To register:\n`$register <account>`"
        end
      end
      
      self.command :register do |event, account_name, chain = :steem|
        mongo_behind_warning(event)
        account = find_account(account_name, event)
        discord_id = event.author.id
        
        return nil unless !!account
        
        cb_account = Cosgrove::Account.new(account.name, chain)
        
        if cb_account.discord_ids.include? discord_id
          event.respond "Already registered `#{account.name}` on `#{chain.upcase}` with <@#{discord_id}>"
          return
        end
        
        memo_key = cb_account.memo_key(discord_id)
        op = SteemData::AccountOperation.type('transfer').
          where(account: steem_account, from: account.name, to: steem_account, memo: {'$regex' => ".*#{memo_key}.*"}).last
          
        if op.nil?
          # Fall back to RPC.  The transaction is so new, SteemData hasn't seen it
          # yet, SteemData is behind, or there is no such transfer.
          
          response = api(chain).get_account_history(steem_account, -1000, 1000)
          op = response.result.map do |history|
            e = history.last.op
            type = e.first
            next unless type == 'transfer'
            o = e.last
            next unless o.from == account.name
            next unless o.to == steem_account
            next unless o.memo =~ /.*#{memo_key}.*/
            
            o
          end.compact.last
        end
          
        if !!op
          cb_account.add_discord_id(discord_id)
          
          if !!@on_success_register_job
            begin
              @on_success_register_job.call(event, cb_account)
            rescue => e
              ap e
              ap e.backtrace
            end
          end
          
          "Ok.  #{chain.to_s.upcase} account #{account.name} has been registered with <@#{discord_id}>."
        else
          "To register `#{account.name}` with <@#{discord_id}>, send `0.001 #{chain.upcase}` to `#{steem_account}` with memo: `#{memo_key}`\n\nThen type `$register #{account.name}` again."
        end
      end
      
      self.command(:upvote, bucket: :voting, rate_limit_message: 'Sorry, you are in cool-down.  Please wait %time% more seconds.') do |event, slug = nil|
        slug = Cosgrove::latest_steemit_link[event.channel.name] if slug.nil? || slug.empty? || slug == '^'
        options = {
          on_success: @on_success_upvote_job
        }

        Cosgrove::UpvoteJob.new(options).perform(event, slug)
      end
    end
    
    def rebuild_accounts
      chain = :steem
      puts "Rebuilding #{Account::ACCOUNTS_FILE} ..."
      
      self.run(:async)
      discord_users = self.users
      ops = SteemData::AccountOperation.where(account: steem_account, to: steem_account, memo: {'$regex' => /.*[a-z]*-[a-z]*-[a-z]*.*/})
      memo_map = {}
      
      puts "Discord users: #{discord_users.size}; memos to search: #{ops.count}"
      
      ops.each do |op|
        memo_map[op.from] = op.memo
      end
      
      discord_users.each do |discord_id, discord_user|
        a = Account.find_by_discord_id(discord_id)
        next if !!a
        
        puts "Account not found: #{discord_user.username}"
        
        memo_map.each do |from, memo|
          a = Account.new(from, chain)
          if memo == a.memo_key(discord_id)
            puts "Found matching memo key: #{from}"
          end
        end
      end
    end
  end
end