// Copyright 2016-2023, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package workspace

import (
	"errors"
	"regexp"
	"strings"

	"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
)

const (
	defaultProjectName = "project"
)

// ValidateProjectName ensures a project name is valid, if it is not it returns an error with a message suitable
// for display to an end user.
func ValidateProjectName(s string) error {
	if err := tokens.ValidateProjectName(s); err != nil {
		return err
	}

	// This is needed to stop cyclic imports in DotNet projects
	if strings.ToLower(s) == "pulumi" || strings.HasPrefix(strings.ToLower(s), "pulumi.") {
		return errors.New("project name must not be `Pulumi` and must not start with the prefix `Pulumi.` " +
			"to avoid collision with standard libraries")
	}

	return nil
}

// ValueOrSanitizedDefaultProjectName returns the value or a sanitized valid project name
// based on defaultNameToSanitize.
func ValueOrSanitizedDefaultProjectName(name string, projectName string, defaultNameToSanitize string) string {
	// If we have a name, use it.
	if name != "" {
		return name
	}

	// If the project already has a name that isn't a replacement string, use it.
	if projectName != "${PROJECT}" {
		return projectName
	}

	// Otherwise, get a sanitized version of `defaultNameToSanitize`.
	return getValidProjectName(defaultNameToSanitize)
}

// ValueOrDefaultProjectDescription returns the value or defaultDescription.
func ValueOrDefaultProjectDescription(
	description string, projectDescription string, defaultDescription string,
) string {
	// If we have a description, use it.
	if description != "" {
		return description
	}

	// If the project already has a description that isn't a replacement string, use it.
	if projectDescription != "${DESCRIPTION}" {
		return projectDescription
	}

	// Otherwise, use the default, which may be an empty string.
	return defaultDescription
}

// getValidProjectName returns a valid project name based on the passed-in name.
func getValidProjectName(name string) string {
	// If the name is valid, return it.
	if ValidateProjectName(name) == nil {
		return name
	}

	// Strip any invalid chars from the name.
	r := regexp.MustCompile("[^a-zA-Z0-9_.-]")
	name = r.ReplaceAllString(name, "")

	// See if the name is now valid
	if ValidateProjectName(name) == nil {
		return name
	}

	// If we couldn't come up with a valid project name, fallback to a default.
	return defaultProjectName
}

// ValidateProjectDescription ensures a project description name is valid, if it is not it returns an error with a
// message suitable for display to an end user.
func ValidateProjectDescription(s string) error {
	const maxTagValueLength = 256

	if len(s) > maxTagValueLength {
		return errors.New("a project description must be 256 characters or less")
	}

	return nil
}