package services_test

import (
	"log"
	"net/http"
	"testing"

	"github.com/jarcoal/httpmock"
	"github.com/onsi/ginkgo/v2"
	"github.com/onsi/gomega"

	"github.com/nicholas-fedor/shoutrrr/pkg/router"
	"github.com/nicholas-fedor/shoutrrr/pkg/types"
)

var serviceURLs = map[string]string{
	"discord":    "discord://token@id",
	"gotify":     "gotify://example.com/Aaa.bbb.ccc.ddd",
	"googlechat": "googlechat://chat.googleapis.com/v1/spaces/FOO/messages?key=bar&token=baz",
	"hangouts":   "hangouts://chat.googleapis.com/v1/spaces/FOO/messages?key=bar&token=baz",
	"ifttt":      "ifttt://key?events=event",
	"join":       "join://:apikey@join/?devices=device",
	"logger":     "logger://",
	"mattermost": "mattermost://user@example.com/token",
	"opsgenie":   "opsgenie://example.com/token?responders=user:dummy",
	"pushbullet": "pushbullet://tokentokentokentokentokentokentoke",
	"pushover":   "pushover://:token@user/?devices=device",
	"rocketchat": "rocketchat://example.com/token/channel",
	"slack":      "slack://AAAAAAAAA/BBBBBBBBB/123456789123456789123456",
	"smtp":       "smtp://host.tld:25/?fromAddress=from@host.tld&toAddresses=to@host.tld",
	"teams":      "teams://?host=https%3A%2F%2Fprod-00.westus.logic.azure.com%3A443%2Fworkflows%2F00000000-0000-0000-0000-000000000000%2Ftriggers%2Fmanual%2Fpaths%2Finvoke%3Fapi-version%3D2016-06-00%26sp%3D%2Ftriggers%2Fmanual%2Frun%26sv%3D1.0%26sig%3DXXXXXXXX",
	"telegram":   "telegram://000000000:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@telegram?channels=channel",
	"twilio":     "twilio://ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:authToken@+15551234567/+15559876543",
	"xmpp":       "xmpp://",
	"zulip":      "zulip://mail:key@example.com/?stream=foo&topic=bar",
}

var serviceResponses = map[string]string{
	"discord":    "",
	"gotify":     `{"id": 0}`,
	"googlechat": "",
	"hangouts":   "",
	"ifttt":      "",
	"join":       "",
	"logger":     "",
	"mattermost": "",
	"opsgenie":   "",
	"pushbullet": `{"type": "note", "body": "test", "title": "test title", "active": true, "created": 0}`,
	"pushover":   "",
	"rocketchat": "",
	"slack":      "",
	"smtp":       "",
	"teams":      "",
	"telegram":   "",
	"twilio":     `{"sid": "SM123"}`,
	"xmpp":       "",
	"zulip":      "",
}

var logger = log.New(ginkgo.GinkgoWriter, "Test", log.LstdFlags)

var _ = ginkgo.Describe("services", func() {
	ginkgo.BeforeEach(func() {
	})
	ginkgo.AfterEach(func() {
	})

	ginkgo.When("passed the a title param", func() {
		var serviceRouter *router.ServiceRouter

		ginkgo.AfterEach(func() {
			httpmock.DeactivateAndReset()
		})

		for key, serviceURL := range serviceURLs {
			serviceRouter, _ = router.NewWithOptions(logger, types.SenderOptions{})

			ginkgo.It("should not throw an error for "+key, func() {
				if key == "smtp" {
					ginkgo.Skip("smtp does not use HTTP and needs a specific test")
				}

				if key == "xmpp" {
					ginkgo.Skip("not supported")
				}

				service, err := serviceRouter.Locate(serviceURL)
				gomega.Expect(err).NotTo(gomega.HaveOccurred())

				httpmock.Activate()

				customClient := &http.Client{}
				if setter, ok := service.(types.HTTPClientSetter); ok {
					httpmock.ActivateNonDefault(customClient)
					setter.SetHTTPClient(customClient)
				}

				respStatus := http.StatusOK
				if key == "discord" || key == "ifttt" {
					respStatus = http.StatusNoContent
				}

				if key == "twilio" {
					respStatus = http.StatusCreated
				}

				if key == "mattermost" {
					httpmock.RegisterResponder(
						"POST",
						"https://example.com/hooks/token",
						httpmock.NewStringResponder(http.StatusOK, ""),
					)
				} else {
					httpmock.RegisterNoResponder(
						httpmock.NewStringResponder(respStatus, serviceResponses[key]),
					)
				}

				err = service.Send("test", (*types.Params)(&map[string]string{
					"title": "test title",
				}))
				gomega.Expect(err).NotTo(gomega.HaveOccurred())
			})

			if key == "mattermost" {
				ginkgo.It("should not throw an error for "+key+" with DisableTLS", func() {
					modifiedURL := serviceURL + "?disabletls=yes"
					service, err := serviceRouter.Locate(modifiedURL)
					gomega.Expect(err).NotTo(gomega.HaveOccurred())

					httpmock.Activate()

					customClient := &http.Client{}
					if setter, ok := service.(types.HTTPClientSetter); ok {
						httpmock.ActivateNonDefault(customClient)
						setter.SetHTTPClient(customClient)
					}

					httpmock.RegisterResponder(
						"POST",
						"http://example.com/hooks/token",
						httpmock.NewStringResponder(http.StatusOK, ""),
					)

					err = service.Send("test", (*types.Params)(&map[string]string{
						"title": "test title",
					}))
					gomega.Expect(err).NotTo(gomega.HaveOccurred())
				})
			}
		}
	})

	ginkgo.Describe("custom HTTP client injection", func() {
		ginkgo.It("should use the injected client when sending a notification", func() {
			customClient := &http.Client{}
			httpmock.ActivateNonDefault(customClient)
			ginkgo.DeferCleanup(httpmock.DeactivateAndReset)

			httpmock.RegisterResponder(
				"POST",
				"https://example.com/webhook",
				httpmock.NewStringResponder(http.StatusOK, ""),
			)

			sr, err := router.NewWithOptions(
				logger,
				types.SenderOptions{HTTPClient: customClient},
				"generic+https://example.com/webhook",
			)
			gomega.Expect(err).NotTo(gomega.HaveOccurred())

			service, err := sr.Locate("generic+https://example.com/webhook")
			gomega.Expect(err).NotTo(gomega.HaveOccurred())

			err = service.Send("test", nil)
			gomega.Expect(err).NotTo(gomega.HaveOccurred())
		})
	})
})

func TestServices(t *testing.T) {
	t.Parallel()
	gomega.RegisterFailHandler(ginkgo.Fail)
	ginkgo.RunSpecs(t, "Service Compliance Suite")
}
