# frozen_string_literal: true

require "date"
require "test_helper"
require "port_repo"
require "db_port"

class FakeDbForPorts < FakeDB
	def initialize(rows=[])
		super({})
		@rows = rows
	end

	def exec_defer(_, _)
		EMPromise.resolve(@rows)
	end
end

class PortRepoDbTest < Minitest::Test
	def setup
		@output = FullManual.new
	end

	def test_list_returns_db_ports
		rows = [
			{
				"id" => "port1",
				"actual_foc_date" => DateTime.now,
				"updated_at" => DateTime.now,
				"tel" => "+12225551234",
				"customer_id" => "cust1",
				"backend_sgx" => "sgx"
			}
		]
		# Make sure that `processing_status`
		# resolves to "complete"
		sleep 1
		db = FakeDbForPorts.new(rows)
		repo = PortRepo::Db.new(@output, db: db)

		ports = repo.list.sync

		assert_equal 1, ports.length
		assert_kind_of DbPort, ports[0]
		assert_equal "port1", ports[0].id
		assert_equal DbPort::Status::COMPLETE, ports[0].processing_status
	end
	em :test_list_returns_db_ports

	def test_list_empty_when_no_rows
		db = FakeDbForPorts.new([])
		repo = PortRepo::Db.new(@output, db: db)

		ports = repo.list.sync

		assert_equal 0, ports.length
	end
	em :test_list_empty_when_no_rows
end

class PortRepoTickTest < Minitest::Test
	def setup
		@expected_output = Minitest::Mock.new
		@output = MockOutputs.new(@expected_output)
	end

	def test_process_continues_after_individual_port_error
		error = RuntimeError.new("port exploded")

		failing_port = PortRepo::Fake::FakePort.new(
			"T02", "COMPLETE", DateTime.now, DateTime.now,
			"0001", "2223334444", "testroute"
		)
		ok_port = PortRepo::Fake::FakePort.new(
			"T01", "SUBMITTED", nil, DateTime.now - 1,
			"ignored", "9998887777", "testroute"
		)

		failing_step = Minitest::Mock.new(
			PortingStepRepo::Frozen.new(failing_port, output: @output)
		)
		failing_step.expect(:perform_next_step, EMPromise.reject(error))

		ok_step = Minitest::Mock.new(
			PortingStepRepo::Frozen.new(ok_port, output: @output)
		)
		ok_step.expect(:perform_next_step, EMPromise.resolve(nil))

		step_repo = Minitest::Mock.new(PortingStepRepo.new(output: @output))
		step_repo.expect(:find, EMPromise.resolve(failing_step), [failing_port])
		step_repo.expect(:find, EMPromise.resolve(ok_step), [ok_port])

		@expected_output.expect(:error, nil, [failing_port.id, :catch, error])
		@expected_output.expect(
			:info, nil, [failing_port.id, :class, failing_step.class]
		)
		@expected_output.expect(:info, nil, [ok_port.id, :class, ok_step.class])

		repo = PortRepo::Fake.new(@output)
		repo.instance_variable_set(:@step_repo, step_repo)
		repo.stub(:list, EMPromise.resolve([failing_port, ok_port])) do
			repo.process.sync
		end

		@output.verify
	end
	em :test_process_continues_after_individual_port_error
end

class PortRepoBandwidthTest < Minitest::Test
	def setup
		@output = FullManual.new
	end

	def test_list_returns_empty_when_no_ports
		stub_request(
			:get,
			/https:\/\/dashboard\.bandwidth\.com\/v1\.0\/accounts\/.*\/portins/
		).to_return(status: 204, body: "")

		repo = PortRepo::Bandwidth.new(@output)
		ports = repo.list

		assert_equal 0, ports.length
	end

	def test_list_wraps_ports_in_port_with_backend
		stub_request(
			:get,
			/https:\/\/dashboard\.bandwidth\.com\/v1\.0\/accounts\/.*\/portins/
		).to_return(status: 200, body: <<~XML)
			<LNPResponseWrapper>
				<TotalCount>1</TotalCount>
				<Links/>
				<lnpPortInfoForGivenStatus>
					<OrderId>abc123</OrderId>
					<CustomerOrderId>cust456</CustomerOrderId>
					<BillingTelephoneNumber>5551234567</BillingTelephoneNumber>
					<lastModifiedDate>2025-01-13T12:00:00.000Z</lastModifiedDate>
					<OrderDate>2025-01-12T12:00:00.000Z</OrderDate>
					<ProcessingStatus>COMPLETE</ProcessingStatus>
				</lnpPortInfoForGivenStatus>
			</LNPResponseWrapper>
		XML

		repo = PortRepo::Bandwidth.new(@output)
		ports = repo.list

		assert_equal 1, ports.length
		port = ports[0]
		assert_kind_of PortRepo::Bandwidth::PortWithBackend, port
		assert_equal "abc123", port.id
		assert_equal "cust456", port.customer_id
		assert_equal "+15551234567", port.tel
		assert_equal CONFIG[:sgx], port.backend_sgx
	end
end
