# frozen_string_literal: true

require "simplecov"
SimpleCov.start do
	add_filter "/test/"
	enable_coverage :branch
	command_name ENV.fetch("COVERAGE_NAME", "tests")
end

require "minitest/autorun"
require "webmock/minitest"

MMS_PROXY = "https://proxy.test.example.com/"

_saved_argv = ARGV.dup

ARGV[0] = "component"
ARGV[6] = MMS_PROXY

require_relative "../sgx-bwmsgsv2"
ARGV.replace(_saved_argv)

def SGXbwmsgsv2.write_to_stream(s)
	@written ||= []
	@written << s
end

def reset_stanzas!
	SGXbwmsgsv2.instance_variable_set(:@written, [])
end

def reset_redis!
	REDIS.reset!
	REDIS.set("catapult_jid-", "HERE")
	REDIS.set("catapult_jid-+15550000000", "test@example.com")
	REDIS.set("catapult_cred-test@example.com", [
		'account', 'user', 'password', '+15550000000'
	])
end

def written
	SGXbwmsgsv2.instance_variable_get(:@written)
end

def invoke_webhook(payload, extra_env: {})
	with_stubs([
		[
			SGXbwmsgsv2,
			:write,
			->(data) { SGXbwmsgsv2.write_to_stream(data) }
		 ]
	]) do
		handler = WebhookHandler.new
		env = {
			"REQUEST_URI" => "/",
			"REQUEST_METHOD" => "POST",
			"params" => { "_json" => [payload] }
		}.merge(extra_env)
		handler.instance_variable_set(:@env, env)
		def handler.params
			@env["params"]
		end
		EMPromise.resolve(nil).then {
			handler.response(env)
		}.sync
	end
end

def process_stanza(s)
	SGXbwmsgsv2.send(:client).receive_data(s)
	raise $panic if $panic
end

def xmpp_error_name(error)
	error.find_first(
		"child::*[name()!='text']",
		Blather::StanzaError::STANZA_ERR_NS
	).element_name
end

def xmpp_error_text(error)
	error.find_first("ns:text", ns: Blather::StanzaError::STANZA_ERR_NS)&.text
end


begin
	require "pry-byebug"

	module Minitest
		class Test
			alias old_capture_exceptions capture_exceptions
			def capture_exceptions
				old_capture_exceptions do
					yield
				rescue Minitest::Skip => e
					failures << e
				end
			end
		end
	end
rescue LoadError, NameError
	# Just helpers for dev, no big deal if missing
	nil
end


$VERBOSE = nil

class FakeRedis
	def initialize(values={})
		@values = values
	end

	def reset!(values={})
		@values = values
	end

	def set(key, value, *)
		@values[key] = value
		EMPromise.resolve("OK")
	end

	def setex(key, _expiry, value)
		set(key, value)
	end

	def del(*keys)
		keys.each { |key| @values.delete(key) }
	end

	def mget(*keys)
		EMPromise.all(keys.map(&method(:get)))
	end

	def get(key)
		EMPromise.resolve(@values[key])
	end

	def getbit(key, bit)
		get(key).then { |v| v.to_i.to_s(2)[bit].to_i }
	end

	def bitfield(key, *ops)
		get(key).then do |v|
			bits = v.to_i.to_s(2)
			ops.each_slice(3).map do |(op, encoding, offset)|
				raise "unsupported bitfield op" unless op == "GET"
				raise "unsupported bitfield op" unless encoding == "u1"

				bits[offset].to_i
			end
		end
	end

	def hget(key, field)
		@values.dig(key, field)
	end

	def hincrby(key, field, incrby)
		@values[key] ||= {}
		@values[key][field] ||= 0
		@values[key][field] += incrby
	end

	def sadd(key, member)
		@values[key] ||= Set.new
		@values[key] << member
	end

	def srem(key, member)
		@values[key].delete(member)
	end

	def scard(key)
		@values[key]&.size || 0
	end

	def expire(_, _); end

	def exists(*keys)
		EMPromise.resolve(
			@values.select { |k, _| keys.include? k }.size.to_s
		)
	end

	def lindex(key, index)
		get(key).then { |v| v&.fetch(index) }
	end

	def lrange(key, sindex, eindex)
		get(key).then { |v| v ? v[sindex..eindex] : [] }
	end

	def rpush(key, *values)
		@values[key] ||= []
		values.each { |v| @values[key].push(v) }
	end

	def multi
	end

	def exec
	end

	def discard
	end

	def xadd(stream, id, *args)
		@values[stream] ||= []
		entry_id = id == "*" ? "#{Time.now.to_i}-0" : id
		fields = Hash[*args]
		@values[stream] << { id: entry_id, fields: fields }
		EMPromise.resolve(entry_id)
	end

	def stream_entries(stream)
		EMPromise.resolve(@values[stream] || [])
	end
end

REDIS = FakeRedis.new

module Minitest
	class Test
		def with_stubs(stubs, &block)
			if stubs.empty?
				block.call
			else
				obj, method, value = stubs.first
				obj.stub(method, value) do
					with_stubs(stubs[1..], &block)
				end
			end
		end

		def self.em(m)
			alias_method "raw_#{m}", m
			define_method(m) do
				$panic = nil
				e = nil
				EM.run do
					Fiber.new {
						ARGV[0] = "component"
						ARGV[6] = MMS_PROXY
						begin
							send("raw_#{m}")
						rescue
							e = $!
						ensure
							EM.stop
						end
					}.resume
				end
				raise e if e
			end
		end
	end
end
