package passwordparser_test

import (
	"strings"

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

	"github.com/cloudfoundry/cloud-service-broker/v2/internal/encryption/passwordparser"
)

var _ = Describe("Parser", func() {
	DescribeTable(
		"correct",
		func(input any, expected any) {
			output, err := passwordparser.Parse(input)
			Expect(err).NotTo(HaveOccurred())
			Expect(output).To(Equal(expected))
		},
		Entry(
			"empty string",
			``,
			[]passwordparser.PasswordEntry(nil),
		),
		Entry(
			"empty list string",
			`[]`,
			[]passwordparser.PasswordEntry{},
		),
		Entry(
			"empty slice",
			[]any{},
			[]passwordparser.PasswordEntry(nil),
		),
		Entry(
			"nil",
			nil,
			[]passwordparser.PasswordEntry(nil),
		),
		Entry(
			"one with minimum lengths",
			`[{"label":"five5","password":{"secret":"01234567890123456789"},"primary":true}]`,
			[]passwordparser.PasswordEntry{
				{
					Label:   "five5",
					Secret:  "01234567890123456789",
					Primary: true,
				},
			},
		),
		Entry(
			"one with maximum lengths",
			`[{"label":"01234567890123456789","password":{"secret":"`+strings.Repeat("a", 1024)+`"},"primary":true}]`,
			[]passwordparser.PasswordEntry{
				{
					Label:   "01234567890123456789",
					Secret:  strings.Repeat("a", 1024),
					Primary: true,
				},
			},
		),
		Entry(
			"one with extra fields",
			`[{"randomz":"extra","label":"barfoo","password":{"secret":"01234567890123456789"},"primary":true}]`,
			[]passwordparser.PasswordEntry{
				{
					Label:   "barfoo",
					Secret:  "01234567890123456789",
					Primary: true,
				},
			},
		),
		Entry(
			"many",
			`[{"label":"barfoo","password":{"secret":"veryverysecretpassword"},"primary":false},{"label":"barbaz","password":{"secret":"anotherveryverysecretpassword"}},{"label":"bazquz","password":{"secret":"yetanotherveryverysecretpassword"},"primary":true}]`,
			[]passwordparser.PasswordEntry{
				{
					Label:   "barfoo",
					Secret:  "veryverysecretpassword",
					Primary: false,
				},
				{
					Label:   "barbaz",
					Secret:  "anotherveryverysecretpassword",
					Primary: false,
				},
				{
					Label:   "bazquz",
					Secret:  "yetanotherveryverysecretpassword",
					Primary: true,
				},
			},
		),
		Entry(
			"no primary",
			`[{"label":"barfoo","password":{"secret":"veryverysecretpassword"},"primary":false},{"label":"barbaz","password":{"secret":"anotherveryverysecretpassword"}},{"label":"bazquz","password":{"secret":"yetanotherveryverysecretpassword"},"primary":false}]`,
			[]passwordparser.PasswordEntry{
				{
					Label:   "barfoo",
					Secret:  "veryverysecretpassword",
					Primary: false,
				},
				{
					Label:   "barbaz",
					Secret:  "anotherveryverysecretpassword",
					Primary: false,
				},
				{
					Label:   "bazquz",
					Secret:  "yetanotherveryverysecretpassword",
					Primary: false,
				},
			},
		),
		Entry(
			"object",
			[]any{map[string]any{"label": "barfoo", "password": map[string]any{"secret": "veryverysecretpassword"}, "primary": true}, map[string]any{"label": "barbaz", "password": map[string]any{"secret": "anotherveryverysecretpassword"}, "primary": false}},
			[]passwordparser.PasswordEntry{
				{
					Label:   "barfoo",
					Secret:  "veryverysecretpassword",
					Primary: true,
				},
				{
					Label:   "barbaz",
					Secret:  "anotherveryverysecretpassword",
					Primary: false,
				},
			},
		),
	)

	DescribeTable(
		"errors",
		func(input any, expected any) {
			output, err := passwordparser.Parse(input)
			Expect(output).To(BeEmpty())
			Expect(err).To(MatchError(expected), err.Error())
		},
		Entry(
			"bad JSON",
			`[{"stuf"`,
			`password reading error: password configuration string could not be parsed as JSON: unexpected end of JSON input`,
		),
		Entry(
			"password length too short",
			`[{"label":"barfoo","password":{"secret":"veryverysecretpassword"},"primary":false},{"label":"barbaz","password":{"secret":"anotherveryverysecretpassword"}},{"label":"bazquz","password":{"secret":"tooshort"},"primary":true}]`,
			`password configuration error: expected value to be 20-1024 characters long, but got length 8: [2].secret.password`,
		),
		Entry(
			"password length too long",
			`[{"label":"barfoo","password":{"secret":"`+strings.Repeat("p", 2000)+`"},"primary":false},{"label":"barbaz","password":{"secret":"anotherveryverysecretpassword"}},{"label":"bazquz","password":{"secret":"yetanotherveryverysecretpassword"},"primary":true}]`,
			`password configuration error: expected value to be 20-1024 characters long, but got length 2000: [0].secret.password`,
		),
		Entry(
			"label length too short",
			`[{"label":"barfoo","password":{"secret":"veryverysecretpassword"},"primary":false},{"label":"bar","password":{"secret":"anotherveryverysecretpassword"}},{"label":"bazquz","password":{"secret":"yetanotherveryverysecretpassword"},"primary":true}]`,
			`password configuration error: expected value to be 5-20 characters long, but got length 3: [1].label`,
		),
		Entry(
			"label length too long",
			`[{"label":"012345678901234567890","password":{"secret":"veryverysecretpassword"},"primary":false},{"label":"barbaz","password":{"secret":"anotherveryverysecretpassword"}},{"label":"bazquz","password":{"secret":"yetanotherveryverysecretpassword"},"primary":true}]`,
			`password configuration error: expected value to be 5-20 characters long, but got length 21: [0].label`,
		),
		Entry(
			"label duplication",
			`[{"label":"barfoo","password":{"secret":"veryverysecretpassword"},"primary":false},{"label":"barbaz","password":{"secret":"anotherveryverysecretpassword"}},{"label":"barfoo","password":{"secret":"yetanotherveryverysecretpassword"},"primary":true}]`,
			`password configuration error: duplicated value, must be unique: barfoo: [2].label`,
		),
		Entry(
			"multiple primaries",
			`[{"label":"barfoo","password":{"secret":"veryverysecretpassword"},"primary":true},{"label":"barbaz","password":{"secret":"anotherveryverysecretpassword"}},{"label":"bazquz","password":{"secret":"yetanotherveryverysecretpassword"},"primary":true}]`,
			`password configuration error: expected exactly one primary, got multiple; mark one password as primary and others as non-primary but do not remove them: [].primary`,
		),
		Entry("invalid type", true, `password reading error: password configuration type error, expected string or object array, got bool`),
	)
})
