#     Copyright 2016 Netflix, Inc.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
require 'posix/spawn'

class ScumblrTask::VulnerabilityCleaner < ScumblrTask::AsyncSidekiq
  include POSIX::Spawn
  def self.task_type_name
    "Vulnerability Cleaner"
  end

  def self.task_category
    "Maintenance"
  end

  def self.worker_class
    return ScumblrWorkers::VulnerabilityCleanerWorker
  end

  def self.options
    return super.merge({
      :saved_result_filter=> {name: "Result Filter",
                              description: "Only run cleaner against matching filter",
                              required: false,
                              type: :saved_result_filter
                              },
      :task_id=> {name: "Remove based on Task id",
                  description: "Removes all vulns associated with task_id",
                  required: false,
                  type: :string
                  },
      :vuln_value => {name: "Remove based on metadata value",
                      description: "Traverse vulnerability metadata and delete all vulns who contain this value",
                      required: false,
                      type: :string
                      }
    })
  end

  def self.description
    "Traverses result vulnerability metadata and deletes vulns based on search criteria"
  end

  def self.config_options
    {
    }
  end

  def initialize(options={})
    super

    unless @options[:task_id].present? or @options[:vuln_value].present?
      raise ScumblrTask::TaskException.new("No search terms provided.")
      return
    end

  end

  def run
    super
  end

end

class ScumblrWorkers::VulnerabilityCleanerWorker < ScumblrWorkers::AsyncSidekiqWorker

  def perform_work(result_id)
    r = Result.find(result_id)

    # Skip results which have no vulnerabilities
    if r.metadata.try(:[], "vulnerabilities").blank?
      return nil
    else
      # Delete all vulns that match the task_id.
      # This is more memory efficent since it doesn't have to
      # recursively crawl the hash.

      if @options[:task_id].present?
        r[:metadata]["vulnerabilities"].delete_if do |vul|
          if vul["task_id"].to_i == @options[:task_id].to_i
            puts r.url
            true
          end
        end
        r.update_vulnerability_counts
        r.save if r.changed?
      else
        # Delete all vulns that match the specified value recrusively.
        r[:metadata]["vulnerabilities"].delete_if do |vul|
            if iterate(vul, @options[:vuln_value]) == "found"
              puts r.url
              true
            end
        end
        r.update_vulnerability_counts
        r.save if r.changed?
      end

      return []
    end
  end

  def iterate(h, term)
    h.each do |k,v|
      # If it's an array value will be k, otherwise value will be v
      value = v || k

      if value.is_a?(Hash) || value.is_a?(Array)
        # Iterate again until we end up with a string
        iterate(value, term)
      elsif value == term
        # Identified term, so we can mark for deletion
        return "found"
      end
    end
  end
end
