require 'optparse'

$stdout.sync = true

module Pallets
  class CLI
    def initialize
      parse_options
      setup_signal_handlers

      @manager = Manager.new
      @signal_queue = Queue.new
    end

    def run
      Pallets.logger.info 'Starting the awesome Pallets <3'
      Pallets.logger.info "Running on #{RUBY_DESCRIPTION}"

      @manager.start

      loop do
        # This blocks until signals are received
        handle_signal(@signal_queue.pop)
      end
    rescue Interrupt
      Pallets.logger.info 'Shutting down...'
      @manager.shutdown
      Pallets.logger.info 'Buh-bye!'
      exit
    end

    private

    def handle_signal(signal)
      case signal
      when 'INT', 'TERM'
        raise Interrupt
      when 'TTIN'
        print_backtraces
      end
    end

    def print_backtraces
      (@manager.workers + [@manager.scheduler]).each do |actor|
        Pallets.logger.info "Debugging #{actor.id}"
        Pallets.logger.info actor.debug.join("\n") unless actor.debug.nil?
      end
    end

    def parse_options
      OptionParser.new do |opts|
        opts.banner = 'Usage: pallets [options]'

        opts.on('-b', '--backend NAME', 'Backend to use') do |backend|
          Pallets.configuration.backend = backend
        end

        opts.on('-c', '--concurrency NUM', Integer, 'Number of workers to start') do |concurrency|
          Pallets.configuration.concurrency = concurrency
        end

        opts.on('-f', '--max-failures NUM', Integer, 'Maximum allowed number of failures per task') do |max_failures|
          Pallets.configuration.max_failures = max_failures
        end

        opts.on('-i', '--scheduler-polling-interval NUM', Integer, 'Seconds between scheduler backend polls') do |scheduler_polling_interval|
          Pallets.configuration.scheduler_polling_interval = scheduler_polling_interval
        end

        opts.on('-l', '--failed-job-lifespan NUM', Integer, 'Seconds a job stays in the given up set') do |failed_job_lifespan|
          Pallets.configuration.failed_job_lifespan = failed_job_lifespan
        end

        opts.on('-m', '--failed-job-max-count NUM', Integer, 'Maximum number of jobs in the given up set') do |failed_job_max_count|
          Pallets.configuration.failed_job_max_count = failed_job_max_count
        end

        opts.on('-p', '--pool-size NUM', Integer, 'Size of backend pool') do |pool_size|
          Pallets.configuration.pool_size = pool_size
        end

        opts.on('-q', '--quiet', 'Output less logs') do
          Pallets.logger.level = Logger::ERROR
        end

        opts.on('-r', '--require PATH', 'Path to file containing workflow definitions or directory containing Rails application') do |path|
          if File.directory?(path)
            require File.expand_path("#{path}/config/environment.rb")
          else
            require(path)
          end
        end

        opts.on('-s', '--serializer NAME', 'Serializer to use') do |serializer|
          Pallets.configuration.serializer = serializer
        end

        opts.on('-t', '--job-timeout NUM', Integer, 'Seconds allowed for a job to be processed') do |job_timeout|
          Pallets.configuration.job_timeout = job_timeout
        end

        opts.on('-u', '--blocking-timeout NUM', Integer, 'Seconds to block while waiting for work') do |blocking_timeout|
          Pallets.configuration.blocking_timeout = blocking_timeout
        end

        opts.on('-v', '--verbose', 'Output more logs') do
          Pallets.logger.level = Logger::DEBUG
        end

        opts.on('--version', 'Version of Pallets') do
          puts "Pallets v#{Pallets::VERSION}"
          exit
        end

        opts.on_tail('-h', '--help', 'Show this message') do
          puts opts
          exit
        end
      end.parse!
    end

    def setup_signal_handlers
      %w(INT TERM TTIN).each do |signal|
        trap signal do
          @signal_queue.push signal
        end
      end
    end
  end
end
