// Copyright 2016-2018, 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 cmd

import (
	"fmt"
	"strings"

	"github.com/pulumi/pulumi/pkg/engine"

	"github.com/pulumi/pulumi/pkg/backend"

	"github.com/pkg/errors"
	"github.com/pulumi/pulumi/pkg/backend/display"
	"github.com/pulumi/pulumi/pkg/backend/httpstate"
	"github.com/pulumi/pulumi/pkg/resource/plugin"
	"github.com/pulumi/pulumi/pkg/util/cmdutil"
	"github.com/pulumi/pulumi/pkg/workspace"
	"github.com/spf13/cobra"
)

func newPolicyPublishCmd() *cobra.Command {
	var cmd = &cobra.Command{
		Use:   "publish [org-name]",
		Args:  cmdutil.MaximumNArgs(1),
		Short: "Publish a Policy Pack to the Pulumi service",
		Long: "Publish a Policy Pack to the Pulumi service\n" +
			"\n" +
			"If an organization name is not specified, the current user account is used.",
		Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {

			var orgName string
			if len(args) > 0 {
				orgName = args[0]
			}

			//
			// Construct a policy pack reference of the form `<org-name>/<policy-pack-name>`
			// with the org name and an empty policy pack name. The policy pack name is empty
			// because it will be determined as part of the publish operation. If the org name
			// is empty, the current user account is used.
			//

			if strings.Contains(orgName, "/") {
				return errors.New("organization name must not contain slashes")
			}
			policyPackRef := fmt.Sprintf("%s/", orgName)

			//
			// Obtain current PolicyPack, tied to the Pulumi service backend.
			//

			policyPack, err := requirePolicyPack(policyPackRef)
			if err != nil {
				return err
			}

			//
			// Load metadata about the current project.
			//

			proj, root, err := readPolicyProject()
			if err != nil {
				return err
			}

			projinfo := &engine.PolicyPackInfo{Proj: proj, Root: root}
			pwd, _, err := projinfo.GetPwdMain()
			if err != nil {
				return err
			}

			plugctx, err := plugin.NewContext(cmdutil.Diag(), cmdutil.Diag(), nil, nil, pwd,
				projinfo.Proj.Runtime.Options(), nil)
			if err != nil {
				return err
			}

			//
			// Attempt to publish the PolicyPack.
			//

			res := policyPack.Publish(commandContext(), backend.PublishOperation{
				Root: root, PlugCtx: plugctx, PolicyPack: proj, Scopes: cancellationScopes})
			if res != nil && res.Error() != nil {
				return res.Error()
			}

			return nil
		}),
	}

	return cmd
}

func requirePolicyPack(policyPack string) (backend.PolicyPack, error) {
	//
	// Attempt to log into cloud backend.
	//

	cloudURL, err := workspace.GetCurrentCloudURL()
	if err != nil {
		return nil, errors.Wrap(err,
			"`pulumi policy` command requires the user to be logged into the Pulumi service")
	}

	displayOptions := display.Options{
		Color: cmdutil.GetGlobalColorization(),
	}

	b, err := httpstate.Login(commandContext(), cmdutil.Diag(), cloudURL, displayOptions)
	if err != nil {
		return nil, err
	}

	//
	// Obtain PolicyPackReference.
	//

	policy, err := b.GetPolicyPack(commandContext(), policyPack, cmdutil.Diag())
	if err != nil {
		return nil, err
	}
	if policy != nil {
		return policy, nil
	}

	return nil, fmt.Errorf("Could not find PolicyPack %q", policyPack)
}
