// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package aesgcm

import (
	"fmt"

	"github.com/opentofu/opentofu/internal/encryption/keyprovider"

	"github.com/opentofu/opentofu/internal/collections"

	"github.com/opentofu/opentofu/internal/encryption/method"
)

// validKeyLengths holds the valid key lengths supported by this method.
var validKeyLengths = collections.NewSet[int](16, 24, 32)

// Config is the configuration for the AES-GCM method.
type Config struct {
	// Key is the encryption key for the AES-GCM encryption. It has to be 16, 24, or 32 bytes long for AES-128, 192, or
	// 256, respectively.
	Keys keyprovider.Output `hcl:"keys" json:"keys" yaml:"keys"`

	// AAD is the Additional Authenticated Data that is authenticated, but not encrypted. In the Go implementation, this
	// data serves as a canary value against replay attacks. The AAD value on decryption must match this setting,
	// otherwise the decryption will fail. (Note: this is Go-specific and differs from the NIST SP 800-38D description
	// of the AAD.)
	AAD []byte `hcl:"aad,optional" json:"aad,omitempty" yaml:"aad,omitempty"`
}

// Build checks the validity of the configuration and returns a ready-to-use AES-GCM implementation.
func (c *Config) Build() (method.Method, error) {
	encryptionKey := c.Keys.EncryptionKey
	decryptionKey := c.Keys.DecryptionKey

	if !validKeyLengths.Has(len(encryptionKey)) {
		return nil, &method.ErrInvalidConfiguration{
			Cause: fmt.Errorf(
				"AES-GCM requires the key length to be one of: %s, received %d bytes in the encryption key",
				validKeyLengths.String(),
				len(encryptionKey),
			),
		}
	}

	if len(decryptionKey) > 0 {
		if !validKeyLengths.Has(len(decryptionKey)) {
			return nil, &method.ErrInvalidConfiguration{
				Cause: fmt.Errorf(
					"AES-GCM requires the key length to be one of: %s, received %d bytes in the decryption key",
					validKeyLengths.String(),
					len(decryptionKey),
				),
			}
		}
	}

	return &aesgcm{
		encryptionKey,
		decryptionKey,
		c.AAD,
	}, nil
}
