package nostr

import (
	"encoding/hex"
	"testing"
	"unsafe"

	"github.com/stretchr/testify/require"
)

func TestParseMessage(t *testing.T) {
	testCases := []struct {
		Name             string
		Message          string
		ExpectedEnvelope Envelope
	}{
		{
			Name:             "nil",
			Message:          "",
			ExpectedEnvelope: nil,
		},
		{
			Name:             "invalid string",
			Message:          "invalid input",
			ExpectedEnvelope: nil,
		},
		{
			Name:             "invalid string with a comma",
			Message:          "invalid, input",
			ExpectedEnvelope: nil,
		},
		{
			Name:             "EVENT envelope with subscription id",
			Message:          `["EVENT","_",{"kind":1,"id":"dc90c95f09947507c1044e8f48bcf6350aa6bff1507dd4acfc755b9239b5c962","pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1644271588,"tags":[],"content":"now that https://blueskyweb.org/blog/2-7-2022-overview was announced we can stop working on nostr?","sig":"230e9d8f0ddaf7eb70b5f7741ccfa37e87a455c9a469282e3464e2052d3192cd63a167e196e381ef9d7e69e9ea43af2443b839974dc85d8aaab9efe1d9296524"}]`,
			ExpectedEnvelope: &EventEnvelope{SubscriptionID: ptr("_"), Event: Event{Kind: 1, ID: MustIDFromHex("dc90c95f09947507c1044e8f48bcf6350aa6bff1507dd4acfc755b9239b5c962"), PubKey: MustPubKeyFromHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"), CreatedAt: 1644271588, Tags: Tags{}, Content: "now that https://blueskyweb.org/blog/2-7-2022-overview was announced we can stop working on nostr?", Sig: mustSigFromHex("230e9d8f0ddaf7eb70b5f7741ccfa37e87a455c9a469282e3464e2052d3192cd63a167e196e381ef9d7e69e9ea43af2443b839974dc85d8aaab9efe1d9296524")}},
		},
		{
			Name:             "EVENT envelope without subscription id",
			Message:          `["EVENT",{"kind":1,"id":"dc90c95f09947507c1044e8f48bcf6350aa6bff1507dd4acfc755b9239b5c962","pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1644271588,"tags":[],"content":"now that https://blueskyweb.org/blog/2-7-2022-overview was announced we can stop working on nostr?","sig":"230e9d8f0ddaf7eb70b5f7741ccfa37e87a455c9a469282e3464e2052d3192cd63a167e196e381ef9d7e69e9ea43af2443b839974dc85d8aaab9efe1d9296524"}]`,
			ExpectedEnvelope: &EventEnvelope{Event: Event{Kind: 1, ID: MustIDFromHex("dc90c95f09947507c1044e8f48bcf6350aa6bff1507dd4acfc755b9239b5c962"), PubKey: MustPubKeyFromHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"), CreatedAt: 1644271588, Tags: Tags{}, Content: "now that https://blueskyweb.org/blog/2-7-2022-overview was announced we can stop working on nostr?", Sig: mustSigFromHex("230e9d8f0ddaf7eb70b5f7741ccfa37e87a455c9a469282e3464e2052d3192cd63a167e196e381ef9d7e69e9ea43af2443b839974dc85d8aaab9efe1d9296524")}},
		},
		{
			Name:             "EVENT envelope with tags",
			Message:          `["EVENT",{"kind":3,"id":"9e662bdd7d8abc40b5b15ee1ff5e9320efc87e9274d8d440c58e6eed2dddfbe2","pubkey":"373ebe3d45ec91977296a178d9f19f326c70631d2a1b0bbba5c5ecc2eb53b9e7","created_at":1644844224,"tags":[["p","3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"],["e","75fc5ac2487363293bd27fb0d14fb966477d0f1dbc6361d37806a6a740eda91e"],["p","46d0dfd3a724a302ca9175163bdf788f3606b3fd1bb12d5fe055d1e418cb60ea"]],"content":"{\"wss://nostr-pub.wellorder.net\":{\"read\":true,\"write\":true},\"wss://nostr.bitcoiner.social\":{\"read\":false,\"write\":true},\"wss://expensive-relay.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relayer.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relay.bitid.nz\":{\"read\":true,\"write\":true},\"wss://nostr.rocks\":{\"read\":true,\"write\":true}}","sig":"811355d3484d375df47581cb5d66bed05002c2978894098304f20b595e571b7e01b2efd906c5650080ffe49cf1c62b36715698e9d88b9e8be43029a2f3fa66be"}]`,
			ExpectedEnvelope: &EventEnvelope{Event: Event{Kind: 3, ID: MustIDFromHex("9e662bdd7d8abc40b5b15ee1ff5e9320efc87e9274d8d440c58e6eed2dddfbe2"), PubKey: MustPubKeyFromHex("373ebe3d45ec91977296a178d9f19f326c70631d2a1b0bbba5c5ecc2eb53b9e7"), CreatedAt: 1644844224, Tags: Tags{Tag{"p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"}, Tag{"e", "75fc5ac2487363293bd27fb0d14fb966477d0f1dbc6361d37806a6a740eda91e"}, Tag{"p", "46d0dfd3a724a302ca9175163bdf788f3606b3fd1bb12d5fe055d1e418cb60ea"}}, Content: "{\"wss://nostr-pub.wellorder.net\":{\"read\":true,\"write\":true},\"wss://nostr.bitcoiner.social\":{\"read\":false,\"write\":true},\"wss://expensive-relay.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relayer.fiatjaf.com\":{\"read\":true,\"write\":true},\"wss://relay.bitid.nz\":{\"read\":true,\"write\":true},\"wss://nostr.rocks\":{\"read\":true,\"write\":true}}", Sig: mustSigFromHex("811355d3484d375df47581cb5d66bed05002c2978894098304f20b595e571b7e01b2efd906c5650080ffe49cf1c62b36715698e9d88b9e8be43029a2f3fa66be")}},
		},
		{
			Name:             "NOTICE envelope",
			Message:          `["NOTICE","kjasbdlasvdluiasvd\"kjasbdksab\\d"]`,
			ExpectedEnvelope: ptr(NoticeEnvelope("kjasbdlasvdluiasvd\"kjasbdksab\\d")),
		},
		{
			Name:             "EOSE envelope",
			Message:          `["EOSE","kjasbdlasvdluiasvd\"kjasbdksab\\d"]`,
			ExpectedEnvelope: ptr(EOSEEnvelope("kjasbdlasvdluiasvd\"kjasbdksab\\d")),
		},
		{
			Name:             "COUNT envelope",
			Message:          `["COUNT","z",{"count":12}]`,
			ExpectedEnvelope: &CountEnvelope{SubscriptionID: "z", Count: ptr(uint32(12))},
		},
		{
			Name:             "COUNT envelope with HLL",
			Message:          `["COUNT","sub1",{"count":42, "hll": "0100000101000000000000040000000001020000000002000000000200000003000002040000000101020001010000000000000007000004010000000200040000020400000000000102000002000004010000010000000301000102030002000301000300010000070000000001000004000102010000000400010002000000000103000100010001000001040100020001000000000000010000020000000000030100000001000400010000000000000901010100000000040000000b030000010100010000010000010000000003000000000000010003000100020000000000010000010100000100000104000200030001000300000001000101000102"}]`,
			ExpectedEnvelope: &CountEnvelope{SubscriptionID: "sub1", Count: ptr(uint32(42)), HyperLogLog: []byte{1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 2, 4, 0, 0, 0, 1, 1, 2, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 4, 1, 0, 0, 0, 2, 0, 4, 0, 0, 2, 4, 0, 0, 0, 0, 0, 1, 2, 0, 0, 2, 0, 0, 4, 1, 0, 0, 1, 0, 0, 0, 3, 1, 0, 1, 2, 3, 0, 2, 0, 3, 1, 0, 3, 0, 1, 0, 0, 7, 0, 0, 0, 0, 1, 0, 0, 4, 0, 1, 2, 1, 0, 0, 0, 4, 0, 1, 0, 2, 0, 0, 0, 0, 1, 3, 0, 1, 0, 1, 0, 1, 0, 0, 1, 4, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0, 0, 0, 9, 1, 1, 1, 0, 0, 0, 0, 4, 0, 0, 0, 11, 3, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 1, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 4, 0, 2, 0, 3, 0, 1, 0, 3, 0, 0, 0, 1, 0, 1, 1, 0, 1, 2}},
		},
		{
			Name:             "OK envelope success",
			Message:          `["OK","3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefaaaaa",true,""]`,
			ExpectedEnvelope: &OKEnvelope{EventID: MustIDFromHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefaaaaa"), OK: true, Reason: ""},
		},
		{
			Name:             "OK envelope failure",
			Message:          `["OK","3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefaaaaa",false,"error: could not connect to the database"]`,
			ExpectedEnvelope: &OKEnvelope{EventID: MustIDFromHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefaaaaa"), OK: false, Reason: "error: could not connect to the database"},
		},
		{
			Name:             "CLOSED envelope with underscore",
			Message:          `["CLOSED","_","error: something went wrong"]`,
			ExpectedEnvelope: &ClosedEnvelope{SubscriptionID: "_", Reason: "error: something went wrong"},
		},
		{
			Name:             "CLOSED envelope with colon",
			Message:          `["CLOSED",":1","auth-required: take a selfie and send it to the CIA"]`,
			ExpectedEnvelope: &ClosedEnvelope{SubscriptionID: ":1", Reason: "auth-required: take a selfie and send it to the CIA"},
		},
		{
			Name:             "AUTH envelope with challenge",
			Message:          `["AUTH","kjsabdlasb aslkd kasndkad \"as.kdnbskadb"]`,
			ExpectedEnvelope: &AuthEnvelope{Challenge: ptr("kjsabdlasb aslkd kasndkad \"as.kdnbskadb")},
		},
		{
			Name:             "AUTH envelope with event",
			Message:          `["AUTH",{"kind":1,"id":"ae1fc7154296569d87ca4663f6bdf448c217d1590d28c85d158557b8b43b4d69","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","created_at":1683660344,"tags":[],"content":"hello world","sig":"94e10947814b1ebe38af42300ecd90c7642763896c4f69506ae97bfdf54eec3c0c21df96b7d95daa74ff3d414b1d758ee95fc258125deebc31df0c6ba9396a51"}]`,
			ExpectedEnvelope: &AuthEnvelope{Event: Event{Kind: 1, ID: MustIDFromHex("ae1fc7154296569d87ca4663f6bdf448c217d1590d28c85d158557b8b43b4d69"), PubKey: MustPubKeyFromHex("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"), CreatedAt: 1683660344, Tags: Tags{}, Content: "hello world", Sig: mustSigFromHex("94e10947814b1ebe38af42300ecd90c7642763896c4f69506ae97bfdf54eec3c0c21df96b7d95daa74ff3d414b1d758ee95fc258125deebc31df0c6ba9396a51")}},
		},
		{
			Name:             "REQ envelope",
			Message:          `["REQ","million", {"kinds": [1]}, {"kinds": [30023 ], "#d": ["buteko",    "batuke"]}]`,
			ExpectedEnvelope: &ReqEnvelope{SubscriptionID: "million", Filters: []Filter{{Kinds: []Kind{1}}, {Kinds: []Kind{30023}, Tags: TagMap{"d": []string{"buteko", "batuke"}}}}},
		},
		{
			Name:             "CLOSE envelope",
			Message:          `["CLOSE","subscription123"]`,
			ExpectedEnvelope: ptr(CloseEnvelope("subscription123")),
		},
		{
			Name:             "AUTH envelope from nak 23gmt bug",
			Message:          `["AUTH","c45b2b06ad92e28a"]`,
			ExpectedEnvelope: &AuthEnvelope{Challenge: ptr("c45b2b06ad92e28a")},
		},
		{
			Name:             "REQ from jumble",
			Message:          `["REQ","sub:1",{"kinds":[1,6],"limit":100}]`,
			ExpectedEnvelope: &ReqEnvelope{SubscriptionID: "sub:1", Filters: []Filter{{Kinds: []Kind{1, 6}, Limit: 100}}},
		},
	}

	t.Run("standard", func(t *testing.T) {
		for _, testCase := range testCases {
			t.Run(testCase.Name, func(t *testing.T) {
				envelope, err := ParseMessage(testCase.Message)
				if testCase.ExpectedEnvelope == nil && envelope == nil {
					return
				}

				if testCase.ExpectedEnvelope == nil {
					require.Nil(t, envelope, "expected nil but got %v", envelope)
					return
				}

				require.NoError(t, err)
				require.NotNil(t, envelope, "expected non-nil envelope but got nil")
				require.Equal(t, testCase.ExpectedEnvelope.String(), envelope.String())
			})
		}
	})
}

func mustSigFromHex(sigStr string) [64]byte {
	var sig [64]byte
	hex.Decode(sig[:], unsafe.Slice(unsafe.StringData(sigStr), 128))
	return sig
}
