# frozen_string_literal: true

require "test_helper"
require_relative "../sgx-bwmsgsv2"

def panic(e)
	$panic = e
end

class WebhookHandlerTest < Minitest::Test
	def setup
		reset_stanzas!
		reset_redis!
	end

	def test_inbound_sms_emits_to_stream
		payload = {
			"type" => "message-received",
			"to" => "+15550000000",
			"message" => {
				"id" => "bw-in-123",
				"direction" => "in",
				"owner" => "+15550000000",
				"from" => "+15551234567",
				"to" => ["+15550000000"],
				"time" => "2025-01-13T10:00:00Z",
				"text" => "Hello from outside"
			}
		}

		invoke_webhook(payload)

		entries = REDIS.stream_entries("messages").sync
		assert_equal 1, entries.length

		event = entries.first[:fields]
		assert_equal "in", event["event"]
		assert_equal "+15551234567", event["from"]
		assert_equal JSON.dump(["+15550000000"]), event["to"]
		assert_equal "bw-in-123", event["bandwidth_id"]
		assert_equal "+15550000000", event["owner"]
		assert_equal "Hello from outside", event["body"]
		assert_equal JSON.dump([]), event["media_urls"]
	end
	em :test_inbound_sms_emits_to_stream

	def test_inbound_mms_emits_to_stream_and_filters_smil
		payload = {
			"type" => "message-received",
			"to" => "+15550000000",
			"message" => {
				"id" => "bw-mms-456",
				"direction" => "in",
				"owner" => "+15550000000",
				"from" => "+15551234567",
				"to" => ["+15550000000"],
				"time" => "2025-01-13T10:05:00Z",
				"text" => "Check this out",
				"media" => [
					"https://example.com/image.jpg",
					"https://example.com/file.smil",
					"https://example.com/data.txt",
					"https://example.com/meta.xml"
				]
			}
		}

		invoke_webhook(payload)

		entries = REDIS.stream_entries("messages").sync
		assert_equal 1, entries.length

		event = entries.first[:fields]
		assert_equal "in", event["event"]
		assert_equal JSON.dump(["https://example.com/image.jpg"]), event["media_urls"]
	end
	em :test_inbound_mms_emits_to_stream_and_filters_smil

	def test_message_delivered_emits_to_stream
		payload = {
			"type" => "message-delivered",
			"to" => "+15550000000",
			"message" => {
				"id" => "bw-out-789",
				"direction" => "out",
				"owner" => "+15550000000",
				"from" => "+15550000000",
				"to" => ["+15551234567"],
				"time" => "2025-01-13T10:10:00Z",
				"tag" => "stanza-id-abc extra-data"
			}
		}

		invoke_webhook(payload)

		entries = REDIS.stream_entries("messages").sync
		assert_equal 1, entries.length

		event = entries.first[:fields]
		assert_equal "delivered", event["event"]
		assert_equal "stanza-id-abc", event["stanza_id"]
		assert_equal "bw-out-789", event["bandwidth_id"]
		assert_equal "2025-01-13T10:10:00Z", event["timestamp"]
	end
	em :test_message_delivered_emits_to_stream

	def test_message_failed_emits_to_stream
		payload = {
			"type" => "message-failed",
			"to" => "+15551234567",
			"message" => {
				"id" => "bw-out-999",
				"direction" => "out",
				"owner" => "+15550000000",
				"from" => "+15550000000",
				"to" => ["+15551234567"],
				"time" => "2025-01-13T10:15:00Z",
				"tag" => "failed-stanza-xyz extra",
				"errorCode" => 4720,
				"description" => "Carrier rejected message"
			}
		}

		invoke_webhook(payload)

		entries = REDIS.stream_entries("messages").sync
		assert_equal 1, entries.length

		event = entries.first[:fields]
		assert_equal "failed", event["event"]
		assert_equal "failed-stanza-xyz", event["stanza_id"]
		assert_equal "bw-out-999", event["bandwidth_id"]
		assert_equal "4720", event["error_code"]
		assert_equal "Carrier rejected message", event["error_description"]
		assert_equal "2025-01-13T10:15:00Z", event["timestamp"]
	end
	em :test_message_failed_emits_to_stream

	def test_resend_emits_resend_event_instead_of_in
		payload = {
			"type" => "message-received",
			"message" => {
				"id" => "bw-in-resend-001",
				"direction" => "in",
				"owner" => "+15550000000",
				"from" => "+15551234567",
				"to" => ["+15550000000"],
				"time" => "2025-01-13T10:00:00Z",
				"text" => "Resent message"
			}
		}

		invoke_webhook(
			payload,
			extra_env: { "HTTP_X_JMP_RESEND_OF" => "1736762400000-0" }
		)

		entries = REDIS.stream_entries("messages").sync
		assert_equal 1, entries.length

		event = entries.first[:fields]
		assert_equal "resend", event["event"]
		assert_equal "bwmsgsv2", event["source"]
		assert_equal "+15550000000", event["owner"]
		assert_equal "1736762400000-0", event["original_stream_id"]
		assert_equal "bw-in-resend-001", event["original_bandwidth_id"]
		refute event.key?("from"), "Resend events should not duplicate message fields"
		refute event.key?("body"), "Resend events should not duplicate message fields"
	end
	em :test_resend_emits_resend_event_instead_of_in

	def test_message_received_empty_params_writes_no_stanza
		result = invoke_webhook(nil, extra_env: { "params" => {} })
		assert_equal [200, {}, "OK"], result
		assert_empty written
		entries = REDIS.stream_entries("messages").sync
		assert_equal 0, entries.length
	end
	em :test_message_received_empty_params_writes_no_stanza

	def test_message_received_unregistered_jid_writes_no_stanza
		payload = {
			"type" => "message-received",
			"to" => "+15559999999",
			"message" => {
				"id" => "bw-unreg-001",
				"direction" => "in",
				"owner" => "+15559999999",
				"from" => "+15551234567",
				"to" => ["+15559999999"],
				"time" => "2025-01-13T10:00:00Z",
				"text" => "Hello"
			}
		}

		result = invoke_webhook(payload)
		assert_equal [403, {}, "Customer not found\n"], result
		assert_empty written
		entries = REDIS.stream_entries("messages").sync
		assert_equal 0, entries.length
	end
	em :test_message_received_unregistered_jid_writes_no_stanza

	def test_message_received_single_recipient_text_stanza
		payload = {
			"type" => "message-received",
			"to" => "+15550000000",
			"message" => {
				"id" => "bw-in-txt-001",
				"direction" => "in",
				"owner" => "+15550000000",
				"from" => "+15551234567",
				"to" => ["+15550000000"],
				"time" => "2025-01-13T10:00:00Z",
				"text" => "Hello from outside"
			}
		}

		invoke_webhook(payload)

		assert_equal 1, written.length
		msg = written.first
		assert_instance_of Blather::Stanza::Message, msg
		assert_equal "test@example.com", msg.to.to_s
		assert_equal "+15551234567@component", msg.from.to_s
		assert_equal "Hello from outside", msg.body
		refute msg.find_first("ns:x", ns: "jabber:x:oob")
		refute msg.find_first(
			"ns:addresses",
			ns: "http://jabber.org/protocol/address"
		)
	end
	em :test_message_received_single_recipient_text_stanza

	def test_message_received_zero_recipients_writes_no_stanza
		payload = {
			"type" => "message-received",
			"to" => "+15550000000",
			"message" => {
				"id" => "bw-in-zero-001",
				"direction" => "in",
				"owner" => "+15550000000",
				"from" => "+15551234567",
				"to" => [],
				"time" => "2025-01-13T10:00:00Z",
				"text" => "Hello with empty to"
			}
		}

		result = invoke_webhook(payload)
		assert_equal [400, {}, "Missing params\n"], result
		assert_empty written
		entries = REDIS.stream_entries("messages").sync
		assert_equal 0, entries.length
	end
	em :test_message_received_zero_recipients_writes_no_stanza

	def test_message_received_group_stanza_has_addresses
		payload = {
			"type" => "message-received",
			"to" => "+15550000000",
			"message" => {
				"id" => "bw-in-grp-001",
				"direction" => "in",
				"owner" => "+15550000000",
				"from" => "+15551234567",
				"to" => ["+15550000000", "+15559999999"],
				"time" => "2025-01-13T10:00:00Z",
				"text" => "Hello group"
			}
		}

		invoke_webhook(payload)

		assert_equal 1, written.length
		msg = written.first
		assert_equal "example.com", msg.to.to_s
		assert_equal "+15551234567@component", msg.from.to_s
		assert_equal "Hello group", msg.body

		addrs = msg.find_first("addresses")
		refute_nil addrs
		assert_equal(
			"http://jabber.org/protocol/address", addrs['xmlns']
		)

		address_nodes = addrs.find("address")
		assert_equal 2, address_nodes.length

		jid_addr = address_nodes.detect { |a| a['jid'] }
		assert_equal "to", jid_addr['type']
		assert_equal "test@example.com", jid_addr['jid']

		uri_addr = address_nodes.detect { |a| a['uri'] }
		assert_equal "to", uri_addr['type']
		assert_equal "sms:+15559999999", uri_addr['uri']
		assert_equal "true", uri_addr['delivered']
	end
	em :test_message_received_group_stanza_has_addresses

	def test_message_received_single_recipient_with_media_stanza
		payload = {
			"type" => "message-received",
			"to" => "+15550000000",
			"message" => {
				"id" => "bw-in-media-001",
				"direction" => "in",
				"owner" => "+15550000000",
				"from" => "+15551234567",
				"to" => ["+15550000000"],
				"time" => "2025-01-13T10:00:00Z",
				"text" => "Check this",
				"media" => ["https://example.com/image.jpg"]
			}
		}

		invoke_webhook(payload)

		assert_equal 2, written.length

		oob_msg = written[0]
		assert_equal "test@example.com", oob_msg.to.to_s
		assert_equal "+15551234567@component", oob_msg.from.to_s
		oob_x = oob_msg.find_first("x")
		refute_nil oob_x
		assert_equal "jabber:x:oob", oob_x['xmlns']
		oob_url = oob_x.find_first("url")
		refute_nil oob_url
		assert_equal "https://example.com/image.jpg", oob_url.text

		text_msg = written[1]
		assert_equal "test@example.com", text_msg.to.to_s
		assert_equal "+15551234567@component", text_msg.from.to_s
		assert_equal "Check this", text_msg.body
	end
	em :test_message_received_single_recipient_with_media_stanza

	def test_message_received_empty_body_no_media_returns_400
		payload = {
			"type" => "message-received",
			"to" => "+15550000000",
			"message" => {
				"id" => "bw-in-empty-001",
				"direction" => "in",
				"owner" => "+15550000000",
				"from" => "+15551234567",
				"to" => ["+15550000000"],
				"time" => "2025-01-13T10:00:00Z",
				"text" => ""
			}
		}

		result = invoke_webhook(payload)
		assert_equal [400, {}, "Missing params\n"], result
		assert_empty written
		entries = REDIS.stream_entries("messages").sync
		assert_equal 0, entries.length
	end
	em :test_message_received_empty_body_no_media_returns_400

	def test_message_received_missing_body_no_media_returns_400
		payload = {
			"type" => "message-received",
			"to" => "+15550000000",
			"message" => {
				"id" => "bw-in-nil-001",
				"direction" => "in",
				"owner" => "+15550000000",
				"from" => "+15551234567",
				"to" => ["+15550000000"],
				"time" => "2025-01-13T10:00:00Z"
			}
		}

		result = invoke_webhook(payload)
		assert_equal [400, {}, "Missing params\n"], result
		assert_empty written
		entries = REDIS.stream_entries("messages").sync
		assert_equal 0, entries.length
	end
	em :test_message_received_missing_body_no_media_returns_400
end
