package plugins

import (
	"errors"

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

var _ = Describe("Config Validation", func() {
	Describe("ValidateConfig", func() {
		Context("when manifest has no config schema", func() {
			It("returns an error", func() {
				manifest := &Manifest{
					Name:    "test",
					Author:  "test",
					Version: "1.0.0",
				}
				err := ValidateConfig(manifest, `{"key": "value"}`)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("no configurable options"))
			})
		})

		Context("when manifest has config schema", func() {
			var manifest *Manifest

			BeforeEach(func() {
				manifest = &Manifest{
					Name:    "test",
					Author:  "test",
					Version: "1.0.0",
					Config: &ConfigDefinition{
						Schema: map[string]any{
							"type": "object",
							"properties": map[string]any{
								"apiKey": map[string]any{
									"type":        "string",
									"description": "API key for the service",
									"minLength":   float64(1),
								},
								"timeout": map[string]any{
									"type":    "integer",
									"minimum": float64(1),
									"maximum": float64(300),
								},
								"enabled": map[string]any{
									"type": "boolean",
								},
							},
							"required": []any{"apiKey"},
						},
					},
				}
			})

			It("accepts valid config", func() {
				err := ValidateConfig(manifest, `{"apiKey": "secret123", "timeout": 30}`)
				Expect(err).ToNot(HaveOccurred())
			})

			It("rejects empty config when required fields are missing", func() {
				err := ValidateConfig(manifest, "")
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("apiKey"))

				err = ValidateConfig(manifest, "{}")
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("apiKey"))
			})

			It("rejects config missing required field", func() {
				err := ValidateConfig(manifest, `{"timeout": 30}`)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("apiKey"))
			})

			It("rejects config with wrong type", func() {
				err := ValidateConfig(manifest, `{"apiKey": "secret", "timeout": "not a number"}`)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("timeout"))
			})

			It("rejects config with value out of range", func() {
				err := ValidateConfig(manifest, `{"apiKey": "secret", "timeout": 500}`)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("timeout"))
			})

			It("rejects config with empty required string", func() {
				err := ValidateConfig(manifest, `{"apiKey": ""}`)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("apiKey"))
			})

			It("rejects invalid JSON", func() {
				err := ValidateConfig(manifest, `{invalid json}`)
				Expect(err).To(HaveOccurred())
				var validationErr *ConfigValidationErrors
				Expect(errors.As(err, &validationErr)).To(BeTrue())
				Expect(validationErr.Errors[0].Message).To(ContainSubstring("invalid JSON"))
			})
		})

		Context("with enum values", func() {
			It("accepts valid enum value", func() {
				manifest := &Manifest{
					Name:    "test",
					Author:  "test",
					Version: "1.0.0",
					Config: &ConfigDefinition{
						Schema: map[string]any{
							"type": "object",
							"properties": map[string]any{
								"logLevel": map[string]any{
									"type": "string",
									"enum": []any{"debug", "info", "warn", "error"},
								},
							},
						},
					},
				}
				err := ValidateConfig(manifest, `{"logLevel": "info"}`)
				Expect(err).ToNot(HaveOccurred())
			})

			It("rejects invalid enum value", func() {
				manifest := &Manifest{
					Name:    "test",
					Author:  "test",
					Version: "1.0.0",
					Config: &ConfigDefinition{
						Schema: map[string]any{
							"type": "object",
							"properties": map[string]any{
								"logLevel": map[string]any{
									"type": "string",
									"enum": []any{"debug", "info", "warn", "error"},
								},
							},
						},
					},
				}
				err := ValidateConfig(manifest, `{"logLevel": "verbose"}`)
				Expect(err).To(HaveOccurred())
			})
		})
	})

	Describe("HasConfigSchema", func() {
		It("returns false when config is nil", func() {
			manifest := &Manifest{
				Name:    "test",
				Author:  "test",
				Version: "1.0.0",
			}
			Expect(manifest.HasConfigSchema()).To(BeFalse())
		})

		It("returns false when schema is nil", func() {
			manifest := &Manifest{
				Name:    "test",
				Author:  "test",
				Version: "1.0.0",
				Config:  &ConfigDefinition{},
			}
			Expect(manifest.HasConfigSchema()).To(BeFalse())
		})

		It("returns true when schema is present", func() {
			manifest := &Manifest{
				Name:    "test",
				Author:  "test",
				Version: "1.0.0",
				Config: &ConfigDefinition{
					Schema: map[string]any{
						"type": "object",
					},
				},
			}
			Expect(manifest.HasConfigSchema()).To(BeTrue())
		})
	})
})
