namespace :elasticsearch do
  namespace :deployment_option do
    ELASTICSEARCH_HOST = 'http://ops-elasticsearch04.va.3sca.net:9200'

    PROVIDER_KEY_USER_AGENT_QUERY = {
        query: {
            filtered: { filter: { bool: {
                must_not: {
                    term: {:"@fields.provider_key" => "-"}},
                must: {
                    term: {
                        "3scaleapp" => :backend,
                        environment: :production,
                        service: :nginx }
                }
            }}}
        },
        aggs: {
            group_by_provider_key: {
                terms: {field:  "@fields.provider_key", size: 0},
                aggs: {
                    group_by_http_user_agent: { terms: { field: "@fields.http_user_agent" }},
                    group_by_3scale_user_agent: { terms: { field: "@fields.http_3scale_user_agent" }}}
            },
            distinct_provider_keys: { terms: { field: "@fields.provider_key", size: 0 }},
            count_provider_keys: { cardinality: { field: "@fields.provider_key" }}
        },
        size: 0
    }.freeze

    task :fetch  do |_t,args|
      require 'httpclient'

      dates = (Date.today - 4)..(Date.today)

      indexes = dates.map{|date| "backend-#{date}" }

      host = URI ENV.fetch('ELASTICSEARCH_HOST', ELASTICSEARCH_HOST)
      url = URI.join(host, "#{indexes.join(',')}/_search?pretty")
      client = HTTPClient.new
      client.debug_dev = $stderr if ENV['DEBUG']

      response = client.post(url, body: PROVIDER_KEY_USER_AGENT_QUERY.to_json)

      raise "invalid response: #{response.inspect}" unless HTTP::Status.successful?(response.status)

      puts response.body
    end


    task :analyze, [:file] => [:environment] do |_t, args|
      stats = JSON.parse File.read(args[:file])

      aggregations = stats.fetch('aggregations')

      count = aggregations.fetch('count_provider_keys').fetch('value')

      puts "Total provider keys: #{count}"
      puts

      provider_keys_with_documents = aggregations.fetch('distinct_provider_keys').fetch('buckets').map do |bucket|
        bucket.values
      end.to_h

      percentage = ->(provider_key, documents) do
        ((documents.to_f / provider_keys_with_documents.fetch(provider_key)) * 100).round(2)
      end

      process_user_agents = ->(provider_key, agg) do
        agg
          .fetch('buckets')
          .reject { |bucket| bucket.fetch('key') == '-' }
          .map{ |bucket| [ bucket.fetch('key'), percentage.call(provider_key, bucket.fetch('doc_count'))] }
          .group_by { |(user_agent, value)| user_agent.split(%r{/|;|(\s(v\d|\d))|(-v\d\.)|[\d]\.}).first }
          .map { |user_agent, groups| [user_agent, groups.to_h] }
          .to_h
      end

      group_by_provider_key = aggregations.fetch('group_by_provider_key').fetch('buckets').map do |bucket|
        [
            provider_key = bucket.fetch('key'),
            {
               '3scale' => process_user_agents.call(provider_key, bucket.fetch('group_by_3scale_user_agent')),
               'http' => process_user_agents.call(provider_key, bucket.fetch('group_by_http_user_agent'))
            }
        ]
      end.to_h


      group_values = ->((group, parts)) do
        sum = parts.values.reduce(:+).round(2)

        rows = []

        if sum == 100
          rows << "**#{group}** | #{sum} % "
        else
          rows << "**#{group}** | #{sum} %   |"
          parts.each {|ua, percent| rows << "*#{ua}* | #{percent} %" }
        end

        rows
      end

      print_groups = ->(groups) do
        puts
        puts "User Agent   | Percentage |"
        puts "------------ | -----------:"

        puts groups.map(&group_values)
      end

      deployment_option = ->(group, parts) do
        case group
        when /nginx-sandbox/ then 'on_3scale'
        when /nginx-hostedproxy/ then 'on_3scale'
        when /nginx/ then 'on_premise'
        when /plugin-/ then group.tr('-', '_').gsub(/_node$/, '_nodejs').gsub('dotnet', 'csharp')
        when /PHP/ then 'plugin_php'
        when /Java|Ruby|PHP/ then "plugin_#{group.downcase}"
        when /Python-|PycURL|python-/ then 'plugin_python'
        when /Curl/ then 'plugin_php'
        when 'threescale_perl_client' then 'plugin_perl'
        when 'RestSharp' then 'plugin_csharp'
        when /Mozilla/, /Pingdom.com/ then nil # api docs proxy
        when 'HTTPClient' then 'plugin_ruby'
        end
      end

      invalid_keys = 0
      providers_preferred = {}

      group_by_provider_key.each do |provider_key, agents|
        provider = Account.find_by_provider_key(provider_key)

        unless provider
         puts "Provider key #{provider_key} is invalid"
         puts
         invalid_keys += 1
         next
        end


        threescale = agents.fetch('3scale').presence
        http = agents.fetch('http').presence

        if threescale || http
          puts "# Provider #{provider.org_name} (#{provider.id})"
        end

        # puts "# Provider key #{provider_key}"

        preferred = nil

        if threescale
          puts "## 3scale-User-Agent:"
          print_groups.call(threescale)
          preferred = threescale.find { |group, parts| parts.values.reduce(:+) > 50 }
        end

        providers_preferred[provider] = [provider_key, preferred || ['none', {}]]

        if preferred.presence
          puts
          puts "preferred deployment option: **#{deployment_option.call(*preferred)}**"
          puts
          next
        end

        if http
          puts "## HTTP User-Agent"
          print_groups.call(http)
          preferred ||= http.find { |group, parts| parts.values.reduce(:+) > 50 }
        end

        puts
      end

      puts
      puts "# Summary"
      puts

      puts " ID | Org. Name | Integration Method | Considered/Total | Percentage"
      puts "--- | --------- | ------------------ | ----------------:| ----------:"
      providers_preferred.each do |provider, (key, (group, parts))|
        preferred = [group, parts]
        percentage = (parts.values.reduce(:+) || 0).round
        total = provider_keys_with_documents.fetch(key)
        hits = (total * percentage/100).round
        deployment = deployment_option.call(*preferred).presence

        provider.services.update_all(deployment_option: deployment)

        puts "#{provider.id.to_s.rjust(13, ' ')} | #{provider.org_name.ljust(40)} | #{deployment} | #{hits}/#{total} | #{percentage} % "
      end

      puts
      puts "Total invalid keys: #{invalid_keys}"
    end
  end
end
