/*
   Copyright The containerd Authors.

   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 images

import (
	"errors"
	"fmt"

	"github.com/containerd/containerd/cmd/ctr/commands"
	"github.com/containerd/imgcrypt/cmd/ctr/commands/flags"
	"github.com/containerd/imgcrypt/cmd/ctr/commands/img"
	"github.com/containerd/imgcrypt/images/encryption/parsehelpers"

	"github.com/urfave/cli"
)

var encryptCommand = cli.Command{
	Name:      "encrypt",
	Usage:     "encrypt an image locally",
	ArgsUsage: "[flags] <local> <new name>",
	Description: `Encrypt an image locally.

	Encrypt an image using public keys.
	The user must provide recpients who will be able to decrypt the image using
	their RSA, EC, or GPG-managed private key. If GPG keys are used then the
	user's GPG keyring must hold the public keys of the recipients.
	The user has control over the individual layers and the platforms they are
	associated with and can encrypt them separately. If no layers or platforms are
	specified, all layers for all platforms will be encrypted.
	This tool also allows management of the recipients of the image through changes
	to the list of recipients.
	Once the image has been encrypted it may be pushed to a registry.

    Recipients are declared with the protocol prefix as follows:
    - pgp:<email-address>
    - jwe:<public-key-file-path>
    - pkcs7:<x509-file-path>
`,
	Flags: append(append(commands.RegistryFlags, cli.StringSliceFlag{
		Name:  "recipient",
		Usage: "Recipient of the image is the person who can decrypt it in the form specified above (i.e. jwe:/path/to/key)",
	}, cli.IntSliceFlag{
		Name:  "layer",
		Usage: "The layer to encrypt; this must be either the layer number or a negative number starting with -1 for topmost layer",
	}, cli.BoolFlag{
		Name:  "all-platforms",
		Usage: "encrypt for all platforms; this is the default",
	}, cli.StringSliceFlag{
		Name:  "platform",
		Usage: "For which platform to encrypt; by default encrytion is done for all platforms",
	}), flags.ImageDecryptionFlags...),
	Action: func(context *cli.Context) error {
		local := context.Args().First()
		if local == "" {
			return errors.New("please provide the name of an image to encrypt")
		}

		newName := context.Args().Get(1)
		if newName != "" {
			fmt.Printf("Encrypting %s to %s\n", local, newName)
		} else {
			fmt.Printf("Encrypting %s and replacing it with the encrypted image\n", local)
		}
		client, ctx, cancel, err := commands.NewClient(context)
		if err != nil {
			return err
		}
		defer cancel()

		recipients := context.StringSlice("recipient")
		if len(recipients) == 0 {
			return errors.New("no recipients given -- nothing to do")
		}

		layers32 := img.IntToInt32Array(context.IntSlice("layer"))

		_, descs, err := getImageLayerInfos(client, ctx, local, layers32, context.StringSlice("platform"))
		if err != nil {
			return err
		}

		cc, err := parsehelpers.CreateCryptoConfig(ParseEncArgs(context), descs)
		if err != nil {
			return err
		}

		_, err = encryptImage(client, ctx, local, newName, &cc, layers32, context.StringSlice("platform"))

		return err
	},
}
