# frozen_string_literal: true

require "test_helper"

require "ostruct"

require_relative "../lib/sim_order"

class SIMOrderTest < Minitest::Test
	SIMOrder::DB = Minitest::Mock.new
	Transaction::DB = Minitest::Mock.new

	def setup
		@price = 5000
		@plan_name = "better than no plan"
		@sim_repo = Minitest::Mock.new
		@sim = SIM.new(
			iccid: "123",
			nickname: nil,
			lpa_code: "LPA:abc",
			remaining_usage_kb: 1025,
			remaining_days: 3,
			notes: "no notes"
		)
	end

	def test_for_enough_balance
		assert_kind_of(
			SIMOrder,
			SIMOrder.for(customer(balance: 60), price: @price, plan: @plan_name)
		)
	end

	def test_for_insufficient_balance_with_top_up
		customer = customer(balance: 40)

		LowBalance::AutoTopUp.stub(
			:for,
			OpenStruct.new(can_top_up?: true),
			[customer, @price]
		) do
			assert_kind_of(
				SIMOrder::WithTopUp,
				SIMOrder.for(customer, price: @price, plan: @plan_name)
			)
		end
	end

	def test_for_insufficient_balance_please_top_up
		customer = customer(balance: 40)

		LowBalance::AutoTopUp.stub(
			:for,
			OpenStruct.new(can_top_up?: false),
			[customer, @price]
		) do
			assert_kind_of(
				SIMOrder::PleaseTopUp,
				SIMOrder.for(customer, price: @price, plan: @plan_name)
			)
		end
	end

	def test_complete_nil_nick
		customer = Minitest::Mock.new(customer("123", balance: 100))
		customer.expect(
			:stanza_from,
			EMPromise.resolve(nil),
			[Blather::Stanza::Message]
		)

		SIMOrder::DB.expect(
			:transaction,
			EMPromise.resolve(@sim)
		) do |&blk|
			blk.call
		end

		Transaction::DB.expect(
			:exec,
			nil,
			[
				String,
				Matching.new { |params|
					assert_equal "123", params[0]
					assert_equal "tx123", params[1]
					assert_kind_of Time, params[2]
					assert_kind_of Time, params[3]
					assert_in_delta(-(@price / 100).to_d, params[4], 0.05)
					assert_equal "SIM Activation 123", params[5]
				}
			]
		)

		@sim_repo.expect(:available, EMPromise.resolve(@sim), [])
		@sim_repo.expect(:put_owner, nil, [@sim, customer, "SIM"])
		@sim_repo.expect(
			:refill,
			EMPromise.resolve(OpenStruct.new(ack: "success", transaction_id: "tx123"))
		) do |refill_sim, amount_mb:|
			@sim == refill_sim && amount_mb == 1024
		end

		order_form = Blather::Stanza::Iq::Command.new.tap { |iq|
			iq.form.fields = [
				{ var: "addr", value: "123 Main St" },
				{ var: "nickname", value: nil }
			]
		}

		blather = Minitest::Mock.new
		blather.expect(
			:<<,
			EMPromise.resolve(:test_result),
			[Matching.new do |reply|
				assert_equal :completed, reply.status
				assert_equal :info, reply.note_type
				assert_equal(
					"You will receive an notice from support when your SIM ships.",
					reply.note.content
				)
			end]
		)
		sim_order = SIMOrder.for(customer, price: @price, plan: @plan_name)
		sim_order.instance_variable_set(:@sim_repo, @sim_repo)

		assert_equal(
			:test_result,
			execute_command(blather: blather) { sim_order.complete(order_form) }
		)

		assert_mock Transaction::DB
		assert_mock SIMOrder::DB
		assert_mock customer
		assert_mock @sim_repo
		assert_mock blather
	end
	em :test_complete_nil_nick

	def test_complete_nick_present
		customer = Minitest::Mock.new(customer("123", balance: 100))
		customer.expect(
			:stanza_from,
			EMPromise.resolve(nil),
			[Blather::Stanza::Message]
		)
		Transaction::DB.expect(
			:exec,
			nil,
			[
				String,
				Matching.new { |params|
					assert_equal "123", params[0]
					assert_equal "tx123", params[1]
					assert_kind_of Time, params[2]
					assert_kind_of Time, params[3]
					assert_in_delta(-(@price / 100).to_d, params[4], 0.05)
					assert_equal "SIM Activation 123", params[5]
				}
			]
		)

		SIMOrder::DB.expect(
			:transaction,
			@sim
		) do |&blk|
			blk.call
		end

		@sim_repo.expect(:available, EMPromise.resolve(@sim), [])
		@sim_repo.expect(
			:put_owner,
			nil,
			[@sim, customer, "test_nick"]
		)
		@sim_repo.expect(
			:refill,
			EMPromise.resolve(OpenStruct.new(ack: "success", transaction_id: "tx123"))
		) do |refill_sim, amount_mb:|
			@sim == refill_sim && amount_mb == 1024
		end

		order_form = Blather::Stanza::Iq::Command.new.tap { |iq|
			iq.form.fields = [
				{ var: "addr", value: "123 Main St" },
				{ var: "nickname", value: "test_nick" }
			]
		}

		blather = Minitest::Mock.new
		blather.expect(
			:<<,
			EMPromise.resolve(:test_result),
			[Matching.new do |reply|
				assert_equal :completed, reply.status
				assert_equal :info, reply.note_type
				assert_equal(
					"You will receive an notice from support when your SIM ships.",
					reply.note.content
				)
			end]
		)
		sim_order = SIMOrder.for(customer, price: @price, plan: @plan_name)
		sim_order.instance_variable_set(:@sim_repo, @sim_repo)

		assert_equal(
			:test_result,
			execute_command(blather: blather) { sim_order.complete(order_form) }
  )

		assert_mock Transaction::DB
		assert_mock SIMOrder::DB
		assert_mock customer
		assert_mock @sim_repo
		assert_mock blather
	end
	em :test_complete_nick_present
end
