package common

import (
	"fmt"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

	workv1 "open-cluster-management.io/api/work/v1"
)

type Validator struct {
	limit int
}

var ManifestValidator = &Validator{limit: 500 * 1024} // the default manifest limit is 500k.

func (m *Validator) WithLimit(limit int) {
	m.limit = limit
}

func (m *Validator) ValidateManifests(manifests []workv1.Manifest) error {
	if len(manifests) == 0 {
		return apierrors.NewBadRequest("Workload manifests should not be empty")
	}

	totalSize := 0
	for _, manifest := range manifests {
		totalSize += manifest.Size()
	}

	if totalSize > m.limit {
		return fmt.Errorf("the size of manifests is %v bytes which exceeds the %v limit", totalSize, m.limit)
	}

	for _, manifest := range manifests {
		err := validateManifest(manifest.Raw)
		if err != nil {
			return err
		}
	}

	return nil
}

func validateManifest(manifest []byte) error {
	// If the manifest cannot be decoded, return err
	unstructuredObj := &unstructured.Unstructured{}
	err := unstructuredObj.UnmarshalJSON(manifest)
	if err != nil {
		return err
	}

	// The object must have name specified, generateName is not allowed in manifestwork
	if unstructuredObj.GetName() == "" {
		return fmt.Errorf("name must be set in manifest")
	}

	if unstructuredObj.GetGenerateName() != "" {
		return fmt.Errorf("generateName must not be set in manifest")
	}

	return nil
}
