#!/usr/bin/env ruby
# encoding: utf-8
# Copyright: 2015, Dominik Richter

# Adding these directories to the load path is necessary for the require
# statements generated by the proto compiler.
a2_root = File.expand_path('../../../..')
$LOAD_PATH.unshift a2_root
$LOAD_PATH.unshift File.join(a2_root, 'components/compliance-service')
$LOAD_PATH.unshift File.join(a2_root, 'components/nodemanager-service')

require 'minitest/pride'
require 'minitest/spec'
require 'minitest/hooks/default'
require 'json'
require 'pry'
require 'grpc'
require 'deepsort'
require 'set'

module VulcanoTest

  def extract_grpc_field(array_in, field)
    return array_in unless array_in.is_a?(Google::Protobuf::RepeatedField)
    array_in.map { |elem|
      elem[field]
    }
  end

  def array_contains_elem(array_in, elem)
    return array_in unless array_in.is_a?(Google::Protobuf::RepeatedField)
    array_in.include?(elem)
  end

  def assert_suggestions_text(expected, suggestions)
    actual = suggestions['suggestions'].flat_map {|s| s['text']}
    assert_equal(expected, actual, caller)
  end

  def assert_suggestions_text_id_version(expected, suggestions)
    actual = suggestions['suggestions'].flat_map {|s| "%s--%s--%s" % [s['text'], s['id'], s['version']]}
    assert_equal(expected, actual, caller)
  end

  def describe_grpc_req path, rpc_method, message
    stub_opts = {
      channel_args: {
        GRPC::Core::Channel::SSL_TARGET => 'compliance-service'
      }
    }

    stub = path::Stub.new("#{HOST}:#{PORT}", GRPC_CREDS, **stub_opts)
    stub.send(rpc_method, message)
  end
  alias_method :GRPC, :describe_grpc_req

  def ss_describe_grpc_req path, rpc_method, message
    stub_opts = {
      channel_args: {
        GRPC::Core::Channel::SSL_TARGET => 'secrets-service'
      }
    }

    stub = path::Stub.new("#{HOST}:#{SS_PORT}", SS_GRPC_CREDS, **stub_opts)
    stub.send(rpc_method, message)
  end
  alias_method :SS_GRPC, :ss_describe_grpc_req

  def manager_describe_grpc_req path, rpc_method, message
    stub_opts = {
      channel_args: {
        GRPC::Core::Channel::SSL_TARGET => 'nodemanager-service'
      }
    }
    stub = path::Stub.new("#{HOST}:#{MANAGER_PORT}", MANAGER_GRPC_CREDS, **stub_opts)
    stub.send(rpc_method, message)
  end
  alias_method :MANAGER_GRPC, :manager_describe_grpc_req

  # expectations

  UUID_REGEXP = /\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/
  def assert_uuid(actual)
    assert_match(UUID_REGEXP, actual)
  end

  def assert_grpc_error(details, code=nil)
    begin
      yield
    rescue GRPC::BadStatus => error
    end

    refute_nil(error, "expected error from GRPC service")

    case details
    when Regexp
      assert_match(details, error.details)
    else
      assert_equal(details, error.details)
    end

    if code
      assert_equal(code, error.code)
    end
  end

  def assert_equal_json_content(expected, actual)
    assert_equal JSON.parse(expected.to_json), JSON.parse(actual.to_json)
  end

  def assert_equal_json_sorted(expected, actual)
    assert_equal JSON.parse(expected).deep_sort, JSON.parse(actual).deep_sort
  end

  def assert_same_elements(expected, actual)
    assert_equal(expected.to_set, actual.to_set)
  end
end

required_env = %w[ PORT SSL_CERT SSL_KEY SSL_ROOT_CERT ]
missing_env = required_env.reject { |k| ENV[k] }

if missing_env.any?
  abort "ENV variables #{missing_env.join(", ")} are missing!"
end

GRPC_CREDS = GRPC::Core::ChannelCredentials.new(
  File.read(ENV['SSL_ROOT_CERT']),
  File.read(ENV['SSL_KEY']),
  File.read(ENV['SSL_CERT']),
)

SS_GRPC_CREDS = GRPC::Core::ChannelCredentials.new(
  File.read(ENV['SSL_ROOT_CERT']),
  File.read(ENV['SS_SSL_KEY']),
  File.read(ENV['SS_SSL_CERT']),
)

MANAGER_GRPC_CREDS = GRPC::Core::ChannelCredentials.new(
  File.read(ENV['SSL_ROOT_CERT']),
  File.read(ENV['MANAGER_SSL_KEY']),
  File.read(ENV['MANAGER_SSL_CERT']),
)

PORT = ENV["PORT"]
SS_PORT = ENV["SS_PORT"]
HOST = ENV.fetch("HOST", "127.0.0.1")
MARKET_PATH = ENV.fetch("MARKET_PATH", "/tmp/market")
PROFILES_PATH = ENV.fetch("PROFILES_PATH", "/tmp/profiles")
MANAGER_PORT = ENV["MANAGER_PORT"]

files = ARGV
files = Dir['*/**/*.rb'] if files.empty?
all_files = files.map do |f|
  if File.file? f
    f
  else
    Dir["#{f}/**/*_spec.rb"].sort
  end
end.flatten

puts "==> Processing spec files in this order: #{all_files}", ''

Minitest::Test.send(:include, VulcanoTest)
Minitest::Test.send(:extend, VulcanoTest)
Minitest::Test.send(:define_singleton_method, :test_order) { :alpha }
Minitest::Test.send(:make_my_diffs_pretty!)

exit_code = 0

all_files.each do |file|
  require_relative file
  exit_code = 1 unless Minitest.run ["--verbose"]

  Minitest::Runnable.reset
end

exit(exit_code)
