# frozen_string_literal: true

require 'indicina/db'
require 'indicina/logging'
require 'indicina/s3'

require 'parallel'

module Indicina

  class Indexer
    extend Logging

    attr_accessor :definition, :suite, :target_days

    def initialize(suite_name)
      @suite = TestSuite.find_or_create(name: suite_name)

      data = File.read(File.join(suite_folder, "#{suite_name}.json"))
      @definition = Mash.new(JSON.parse(data))

      target_day = suite.indexing_day
      @target_days = [target_day - 60 * 60 * 24, target_day, target_day + 60 * 60 * 24]
    end

    def index
      start_time = Time.now
      important "Indexing #{target_days.collect { |day| day.strftime('%b %d %Y') }.join(', ')} for the #{suite.name} test suite."

      debug 'Scanning s3 for new test results...'

      DB.disconnect # we have to disconnect before forking
      test_keys = Parallel.map(test_prefixes, in_processes: Indicina.parallelize? ? 20 : 0, progress: Indicina.parallelize?) do |prefix|
        debug "Scanning #{prefix}..."
        Indicina.s3_client.objects(prefix: prefix).select { |obj| File.basename(obj.key) == 'definition.json' }.collect(&:key)
      end.flatten

      important "Discovered #{test_keys.length} test results to parse."

      DB.disconnect # we have to disconnect before forking
      Parallel.each(test_keys, in_processes: Indicina.parallelize? ? 20 : 0, progress: Indicina.parallelize?) { |key| Test.from_s3(key, suite) }

      now = Time.now.utc
      suite.update indexing_day: target_days[2] if target_days[1] < Time.new(now.year, now.month, now.day, 0, 0, 0, 0)

      important "Indexed #{test_keys.length} in #{format('%.3f', Time.now - start_time)} seconds."
    end

    # Helpers
    def results_folder
      @results_folder ||= File.join(suite.name, '/')
    end

    def suite_folder
      @suite_folder ||= File.expand_path('../../../suites', __FILE__)
    end

    def test_prefixes
      Enumerator.new do |yield_to|
        target_days.each do |day|
          definition.experiences.each_pair do |experience, variants|
            definition.locations.each do |location|
              definition.browsers&.each do |browser|
                variants.each do |variant|
                  yield_to << prefix(experience, variant.platform, location, browser, day)
                end
              end

              definition.mobile_browsers&.each do |browser|
                variants.each do |variant|
                  yield_to << prefix(experience, variant.platform, location, browser, day)
                end
              end
            end
          end
        end
      end
    end

    def prefix(experience, platform, location, browser, day)
      parts = [
        experience,
        platform,
        location,
        browser
      ]

      parts = [
        definition.name.sanitize,
        parts.collect(&:to_s).collect(&:sanitize).collect(&:downcase),
        day.strftime('%y%m%d')
      ]

      parts.join('/')
    end
  end
end

