# frozen_string_literal: true

module Matterhorn
  module Models
    class Queue < Matterhorn::Model
      attr_reader :id

      def initialize(project, override_active: false)
        @id = project
        @override_active = override_active
      end

      def channel
        @channel ||= "/queue/#{id}"
      end

      def base_queue_name
        @base_queue_name ||= @id.tr('/', ':')
      end

      def priority_queue_name
        @priority_queue_name ||= "#{base_queue_name}:priority"
      end

      def sidekiq_queues
        @sidekiq_queues ||= [
          Sidekiq::Queue.new(priority_queue_name),
          Sidekiq::Queue.new(base_queue_name)
        ]
      end

      def priority_queue
        sidekiq_queues[0]
      end

      def normal_queue
        sidekiq_queues[1]
      end

      def queue
        @queue ||= queued_jobs.keys.reverse # reverse b/c queues are stored in reverse order in redis
      end

      def queued_jobs
        @jobs ||= begin
          jobs = {}
          sidekiq_queues.reverse.each do |sidekiq_queue|
            sidekiq_queue.each do |job|
              jobs[job.jid] = Matterhorn::Models::Job.new(job.jid, job.item, self)
            end
          end
          jobs[active_job.jid] = active_job unless active_job.nil?
          jobs
        end
      end

      def jobs
        queued_jobs.merge(past_jobs)
      end

      def active_job
        return nil if @override_active
        @active_job ||= begin
          job = nil
          Sidekiq::Workers.new.each do |_process_id, _thread_id, work|
            job = Matterhorn::Models::Job.new(work['payload']['jid'], work['payload'], self) \
              if [base_queue_name, priority_queue_name].include?(work['queue'])
          end
          job.status = 'running' unless job.nil? # if we are the active job, we are running
          job
        end
      end

      def processed
        @processed ||= past_jobs.keys
      end

      def past_jobs_key
        "matterhorn:#{base_queue_name}:past-items"
      end

      def past_jobs
        @past_jobs ||= begin
          redis = Redis.new(url: REDIS_URL)
          redis.lrange(past_jobs_key, 0, -1).map { |i| JSON.parse(i) }.map { |j| [j['id'], j] }.to_h
        end
      end

      def is_running
        @is_running ||= {
          priority: !priority_queue.paused?,
          normal: !normal_queue.paused?
        }
      end

      def pause!
        priority_queue.pause!
        normal_queue.pause!
      end

      def unpause!
        priority_queue.unpause!
        normal_queue.unpause!
      end

      def name
        PROJECTS[:projects][@id][:name]
      end

      def simulation_mode?
        !!PROJECTS[:projects][@id][:simulation_only]
      end

      def silent_mode?
        !!PROJECTS[:projects][@id][:silent_mode]
      end

      def includes?(pull_request_number)
        queued_jobs.each_value do |job|
          return true if job.pull_request_number == pull_request_number
        end
        false
      end

      def excludes?(pull_request_number)
        !includes?(pull_request_number)
      end

      def timeout
        @timeout ||= PROJECTS[:projects][@id][:required_check_timeout] || DEFAULT_JOB_TIMEOUT
      end

      def to_json(options = nil)
        data = {
          id: @id,
          channel: channel,
          isRunning: is_running,
          name: name,
          active_job: active_job.nil? ? nil : active_job.id,
          processed: processed,
          queue: queue,
          simulation_mode: simulation_mode?,
          silent_mode: silent_mode?,
          timeout: timeout
        }.to_json(options)
      end

      class << self
        def all
          @all ||= begin
            projects = []
            PROJECTS[:projects].each_key do |project|
              projects << Matterhorn::Models::Queue.new(project)
            end
            projects
          end
        end

        def find_by_platform(platform)
          return {} unless PROJECTS[:platforms].key?(platform.to_sym)
          projects = {}
          PROJECTS[:projects].each_key do |project|
            next unless PROJECTS[:platforms][platform.to_sym].include?(project)
            queue = Matterhorn::Models::Queue.new(project)
            projects[queue.id] = queue
          end
          projects
        end
      end
    end
  end
end
