package teams

import (
	"errors"
	"log"
	"net/url"
	"testing"

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

const (
	extraIDValue     = "V2ESyij_gAljSoUQHvZoZYzlpAoAXExyOl26dlf1xHEx05"
	scopedWebhookURL = "https://test.webhook.office.com/webhookb2/11111111-4444-4444-8444-cccccccccccc@22222222-4444-4444-8444-cccccccccccc/IncomingWebhook/33333301222222222233333333333344/44444444-4444-4444-8444-cccccccccccc/" + extraIDValue
	scopedDomainHost = "test.webhook.office.com"
	testURLBase      = "teams://11111111-4444-4444-8444-cccccccccccc@22222222-4444-4444-8444-cccccccccccc/33333301222222222233333333333344/44444444-4444-4444-8444-cccccccccccc/" + extraIDValue
	scopedURLBase    = testURLBase + "?host=" + scopedDomainHost
)

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

// TestTeams runs the test suite for the Teams package.
func TestTeams(t *testing.T) {
	gomega.RegisterFailHandler(ginkgo.Fail)
	ginkgo.RunSpecs(t, "Shoutrrr Teams Suite")
}

var _ = ginkgo.Describe("the teams service", func() {
	ginkgo.When("creating the webhook URL", func() {
		ginkgo.It("should match the expected output for custom URLs", func() {
			config := Config{}
			config.setFromWebhookParts([5]string{
				"11111111-4444-4444-8444-cccccccccccc",
				"22222222-4444-4444-8444-cccccccccccc",
				"33333301222222222233333333333344",
				"44444444-4444-4444-8444-cccccccccccc",
				extraIDValue,
			})
			apiURL := BuildWebhookURL(
				scopedDomainHost,
				config.Group,
				config.Tenant,
				config.AltID,
				config.GroupOwner,
				config.ExtraID,
			)
			gomega.Expect(apiURL).To(gomega.Equal(scopedWebhookURL))

			parts, err := ParseAndVerifyWebhookURL(apiURL)
			gomega.Expect(err).ToNot(gomega.HaveOccurred())
			gomega.Expect(parts).To(gomega.Equal(config.WebhookParts()))
		})
	})

	ginkgo.Describe("creating a config", func() {
		ginkgo.When("parsing the configuration URL", func() {
			ginkgo.It("should be identical after de-/serialization", func() {
				testURL := testURLBase + "?color=aabbcc&host=test.webhook.office.com&title=Test+title"
				url, err := url.Parse(testURL)
				gomega.Expect(err).NotTo(gomega.HaveOccurred(), "parsing")

				config := &Config{}
				err = config.SetURL(url)
				gomega.Expect(err).NotTo(gomega.HaveOccurred(), "verifying")

				outputURL := config.GetURL()
				gomega.Expect(outputURL.String()).To(gomega.Equal(testURL))
			})
		})
	})

	ginkgo.Describe("converting custom URL to service URL", func() {
		ginkgo.When("an invalid custom URL is provided", func() {
			ginkgo.It("should return an error", func() {
				service := Service{}
				testURL := "teams+https://google.com/search?q=what+is+love"
				customURL, err := url.Parse(testURL)
				gomega.Expect(err).NotTo(gomega.HaveOccurred(), "parsing")

				_, err = service.GetConfigURLFromCustom(customURL)
				gomega.Expect(err).To(gomega.HaveOccurred(), "converting")
			})
		})
		ginkgo.When("a valid custom URL is provided", func() {
			ginkgo.It("should set the host field from the custom URL", func() {
				service := Service{}
				testURL := `teams+` + scopedWebhookURL
				customURL, err := url.Parse(testURL)
				gomega.Expect(err).NotTo(gomega.HaveOccurred(), "parsing")

				serviceURL, err := service.GetConfigURLFromCustom(customURL)
				gomega.Expect(err).NotTo(gomega.HaveOccurred(), "converting")
				gomega.Expect(serviceURL.String()).To(gomega.Equal(scopedURLBase))
			})
			ginkgo.It("should preserve the query params in the generated service URL", func() {
				service := Service{}
				testURL := "teams+" + scopedWebhookURL + "?color=f008c1&title=TheTitle"
				customURL, err := url.Parse(testURL)
				gomega.Expect(err).NotTo(gomega.HaveOccurred(), "parsing")

				serviceURL, err := service.GetConfigURLFromCustom(customURL)
				gomega.Expect(err).NotTo(gomega.HaveOccurred(), "converting")
				expectedURL := testURLBase + "?color=f008c1&host=test.webhook.office.com&title=TheTitle"
				gomega.Expect(serviceURL.String()).To(gomega.Equal(expectedURL))
			})
		})
	})

	ginkgo.Describe("sending the payload", func() {
		var err error
		var service Service
		ginkgo.BeforeEach(func() {
			httpmock.Activate()
		})
		ginkgo.AfterEach(func() {
			httpmock.DeactivateAndReset()
		})
		ginkgo.It("should not report an error if the server accepts the payload", func() {
			serviceURL, _ := url.Parse(scopedURLBase)
			err = service.Initialize(serviceURL, logger)
			gomega.Expect(err).NotTo(gomega.HaveOccurred())

			httpmock.RegisterResponder(
				"POST",
				scopedWebhookURL,
				httpmock.NewStringResponder(200, ""),
			)
			err = service.Send("Message", nil)
			gomega.Expect(err).NotTo(gomega.HaveOccurred())
		})
		ginkgo.It("should not panic if an error occurs when sending the payload", func() {
			serviceURL, _ := url.Parse(testURLBase + "?host=test.webhook.office.com")
			err = service.Initialize(serviceURL, logger)
			gomega.Expect(err).NotTo(gomega.HaveOccurred())

			httpmock.RegisterResponder(
				"POST",
				scopedWebhookURL,
				httpmock.NewErrorResponder(errors.New("dummy error")),
			)
			err = service.Send("Message", nil)
			gomega.Expect(err).To(gomega.HaveOccurred())
		})
	})

	ginkgo.It("should return the correct service ID", func() {
		service := &Service{}
		gomega.Expect(service.GetID()).To(gomega.Equal("teams"))
	})

	// Config tests
	ginkgo.Describe("the teams config", func() {
		ginkgo.Describe("setURL", func() {
			ginkgo.It("should set all fields correctly from URL", func() {
				config := &Config{}
				urlStr := testURLBase + "?title=Test&color=red&host=test.webhook.office.com"
				parsedURL, err := url.Parse(urlStr)
				gomega.Expect(err).NotTo(gomega.HaveOccurred())

				err = config.SetURL(parsedURL)
				gomega.Expect(err).NotTo(gomega.HaveOccurred())

				gomega.Expect(config.Group).To(gomega.Equal("11111111-4444-4444-8444-cccccccccccc"))
				gomega.Expect(config.Tenant).
					To(gomega.Equal("22222222-4444-4444-8444-cccccccccccc"))
				gomega.Expect(config.AltID).To(gomega.Equal("33333301222222222233333333333344"))
				gomega.Expect(config.GroupOwner).
					To(gomega.Equal("44444444-4444-4444-8444-cccccccccccc"))
				gomega.Expect(config.ExtraID).To(gomega.Equal(extraIDValue))
				gomega.Expect(config.Title).To(gomega.Equal("Test"))
				gomega.Expect(config.Color).To(gomega.Equal("red"))
				gomega.Expect(config.Host).To(gomega.Equal("test.webhook.office.com"))
			})

			ginkgo.It("should reject URLs missing the extraID", func() {
				config := &Config{}
				urlStr := "teams://11111111-4444-4444-8444-cccccccccccc@22222222-4444-4444-8444-cccccccccccc/33333301222222222233333333333344/44444444-4444-4444-8444-cccccccccccc?host=test.webhook.office.com"
				parsedURL, err := url.Parse(urlStr)
				gomega.Expect(err).NotTo(gomega.HaveOccurred())

				err = config.SetURL(parsedURL)
				gomega.Expect(err).To(gomega.HaveOccurred())
			})

			ginkgo.It("should require the host parameter", func() {
				config := &Config{}
				urlStr := testURLBase
				parsedURL, err := url.Parse(urlStr)
				gomega.Expect(err).NotTo(gomega.HaveOccurred())

				err = config.SetURL(parsedURL)
				gomega.Expect(err).To(gomega.HaveOccurred())
			})
		})

		ginkgo.Describe("getURL", func() {
			ginkgo.It("should generate correct URL with all parameters", func() {
				config := &Config{
					Group:      "11111111-4444-4444-8444-cccccccccccc",
					Tenant:     "22222222-4444-4444-8444-cccccccccccc",
					AltID:      "33333301222222222233333333333344",
					GroupOwner: "44444444-4444-4444-8444-cccccccccccc",
					ExtraID:    extraIDValue,
					Title:      "Test",
					Color:      "red",
					Host:       "test.webhook.office.com",
				}

				urlObj := config.GetURL()
				urlStr := urlObj.String()
				expectedURL := testURLBase + "?color=red&host=test.webhook.office.com&title=Test"
				gomega.Expect(urlStr).To(gomega.Equal(expectedURL))
			})
		})

		ginkgo.Describe("verifyWebhookParts", func() {
			ginkgo.It("should validate correct webhook parts", func() {
				parts := [5]string{
					"11111111-4444-4444-8444-cccccccccccc",
					"22222222-4444-4444-8444-cccccccccccc",
					"33333301222222222233333333333344",
					"44444444-4444-4444-8444-cccccccccccc",
					extraIDValue,
				}
				err := verifyWebhookParts(parts)
				gomega.Expect(err).NotTo(gomega.HaveOccurred())
			})

			ginkgo.It("should reject invalid group ID", func() {
				parts := [5]string{
					"invalid-id",
					"22222222-4444-4444-8444-cccccccccccc",
					"33333333012222222222333333333344",
					"44444444-4444-4444-8444-cccccccccccc",
					extraIDValue,
				}
				err := verifyWebhookParts(parts)
				gomega.Expect(err).To(gomega.HaveOccurred())
			})
		})

		ginkgo.Describe("parseAndVerifyWebhookURL", func() {
			ginkgo.It("should correctly parse valid webhook URL", func() {
				webhookURL := scopedWebhookURL
				parts, err := ParseAndVerifyWebhookURL(webhookURL)
				gomega.Expect(err).NotTo(gomega.HaveOccurred())
				gomega.Expect(parts).To(gomega.Equal([5]string{
					"11111111-4444-4444-8444-cccccccccccc",
					"22222222-4444-4444-8444-cccccccccccc",
					"33333301222222222233333333333344",
					"44444444-4444-4444-8444-cccccccccccc",
					extraIDValue,
				}))
			})

			ginkgo.It("should reject invalid webhook URL", func() {
				webhookURL := "https://teams.microsoft.com/invalid/webhook/url"
				_, err := ParseAndVerifyWebhookURL(webhookURL)
				gomega.Expect(err).To(gomega.HaveOccurred())
			})
		})
	})
})
