require 'cinch'
require 'redis'
require 'trollop'
require 'uri'

require File.expand_path('../brain', __FILE__)
require File.expand_path('../command_patterns', __FILE__)
require File.expand_path('../finish_notifier', __FILE__)
require File.expand_path('../pipeline_notifier', __FILE__)
require File.expand_path('../../lib/couchdb', __FILE__)

opts = Trollop.options do
  opt :server, 'IRC server, expressed as a URI (irc://SERVER:PORT or ircs://SERVER:PORT for SSL)', :type => String
  opt :nick, 'Nick to use', :default => 'ArchiveBot'
  opt :channels, 'Comma-separated list of channels', :type => String
  opt :sasl, 'SASL PLAIN authentication (USERNAME:PASSWORD)', :type => String, :default => nil
  opt :schemes, 'Comma-separated list of acceptable URI schemes', :default => 'http,https,ftp'
  opt :redis, 'URL of Redis server', :default => ENV['REDIS_URL'] || 'redis://localhost:6379/0'
  opt :password, 'IRC server password', :default => nil, :type => String
  opt :db, 'URL of CouchDB database', :default => ENV['COUCHDB_URL'] || 'http://localhost:5984/archivebot'
  opt :db_credentials, 'Credentials for CouchDB database (USERNAME:PASSWORD)', :type => String, :default => nil
end

redis = Redis.new(:url => opts[:redis])

%w(server nick channels).each do |opt|
  Trollop.die "#{opt} is required" unless opts[opt.to_sym]
end

schemes = opts[:schemes].split(',').map(&:strip)
channels = opts[:channels].split(',').map(&:strip)
uri = URI.parse(opts[:server])

bot = Cinch::Bot.new do
  configure do |c|
    c.server = uri.host
    c.port = uri.port
    c.nick = opts[:nick]
    c.channels = channels
    c.password = opts[:password]
    c.sasl.username, c.sasl.password = opts[:sasl].split(':', 2) unless opts[:sasl].nil?
    c.plugins.plugins = [FinishNotifier, PipelineNotifier]
    c.plugins.options[FinishNotifier] = {
      redis: redis
    }
    c.plugins.options[PipelineNotifier] = {redis: redis}

    if uri.scheme == 'ircs'
      c.ssl.use = true
      c.ssl.verify = true
    end

    c.server_queue_size = 7
    c.messages_per_second = 1.0
  end

  couchdb = Couchdb.new(URI(opts[:db]), opts[:db_credentials])
  brain = Brain.new(schemes, redis, couchdb)

  on :message, CommandPatterns::ARCHIVE do |m, target, params|
    brain.request_archive(m, target, params)
  end

  on :message, CommandPatterns::ARCHIVE_FILE do |m, target, params|
    brain.request_archive(m, target, params, :inf, true)
  end

  on :message, CommandPatterns::ARCHIVEONLY do |m, target, params|
    brain.request_archive(m, target, params, :shallow)
  end

  on :message, CommandPatterns::ARCHIVEONLY_FILE do |m, target, params|
    brain.request_archive(m, target, params, :shallow, true)
  end

  on :message, /\A\!status\s*\Z/ do |m|
    brain.request_summary(m)
  end

  on :message, /\A!status (#{CommandPatterns::IDENT})\s*\Z/ do |m, ident|
    brain.find_job(ident, m) { |j| brain.request_status(m, j) }
  end

  on :message, /\A!status (#{brain.url_pattern})\s*\Z/ do |m, url|
    brain.request_status_by_url(m, url)
  end

  on :message, CommandPatterns::IGNORE do |m, ident, pattern|
    brain.find_job(ident, m) { |j| brain.add_ignore_pattern(m, j, pattern) }
  end

  on :message, CommandPatterns::UNIGNORE do |m, ident, pattern|
    brain.find_job(ident, m) { |j| brain.remove_ignore_pattern(m, j, pattern) }
  end

  on :message, /\A!ig(?:nore)?set (#{CommandPatterns::IDENT}) (.+)/ do |m, ident, sets|
    brain.find_job(ident, m) { |j| brain.add_ignore_sets(m, j, sets) }
  end

  on :message, /\A!expire (#{CommandPatterns::IDENT})\s*\Z/ do |m, ident|
    brain.find_job(ident, m) { |j| brain.expire(m, j) }
  end

  on :message, CommandPatterns::SET_DELAY do |m, ident, min, max|
    brain.find_job(ident, m) { |j| brain.set_delay(j, min, max, m) }
  end

  on :message, CommandPatterns::SET_CONCURRENCY do |m, ident, level|
    brain.find_job(ident, m) { |j| brain.set_concurrency(j, level, m) }
  end

  on :message, /\A!yahoo (#{CommandPatterns::IDENT})\s*\Z/ do |m, ident|
    brain.find_job(ident, m) { |j| brain.yahoo(j, m) }
  end

  on :message, /\A!abort (#{CommandPatterns::IDENT})\s*\Z/ do |m, ident|
    brain.find_job(ident, m) { |j| brain.initiate_abort(m, j) }
  end

  on :message, /\A(?:!igrep|!ignorereports) (#{CommandPatterns::IDENT})\s+(on|off)\s*\Z/ do |m, ident, mode|
    brain.find_job(ident, m) do |j|
      brain.toggle_ignores(m, j, mode == 'on' ? true : false)
    end
  end

  on :message, /\A!igoff (#{CommandPatterns::IDENT})\s*\Z/ do |m, ident|
    brain.find_job(ident, m) { |j| brain.toggle_ignores(m, j, false) }
  end

  on :message, /\A!igon (#{CommandPatterns::IDENT})\s*\Z/ do |m, ident|
    brain.find_job(ident, m) { |j| brain.toggle_ignores(m, j, true) }
  end

  on :message, /\A!pending\s*\Z/ do |m|
    brain.show_pending(m)
  end

  on :message, /\A!stanbush (.+)\s*\Z/ do |m, nick|
    m.channel.send "#{nick}: http://youtu.be/AZKpByV5764?t=3s"
  end

  on :message, /\A!(?:ex(?:plain)?|reason) (#{CommandPatterns::IDENT}) (.+)\Z/ do |m, ident, note|
    brain.find_job(ident, m) { |j| brain.add_note(m, j, note) }
  end

  on :message, CommandPatterns::WHEREIS do |m, ident|
    brain.find_job(ident, m) { |j| brain.whereis(m, j) }
  end
end

bot.start
