/*
Copyright 2024 The Flux 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 main

import (
	"context"
	"fmt"
	"os"

	"github.com/fluxcd/flux2/v2/internal/utils"
	"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
	"github.com/spf13/cobra"
	corev1 "k8s.io/api/core/v1"
	"sigs.k8s.io/yaml"
)

var createSecretGitHubAppCmd = &cobra.Command{
	Use:   "githubapp [name]",
	Short: "Create or update a github app secret",
	Long:  withPreviewNote(`The create secret githubapp command generates a Kubernetes secret that can be used for GitRepository authentication with github app`),
	Example: `  # Create a githubapp authentication secret on disk and encrypt it with Mozilla SOPS
  flux create secret githubapp podinfo-auth \
    --app-id="1" \
    --app-installation-id="2" \
    --app-private-key=./private-key-file.pem \
    --export > githubapp-auth.yaml

  sops --encrypt --encrypted-regex '^(data|stringData)$' \
    --in-place githubapp-auth.yaml
	`,
	RunE: createSecretGitHubAppCmdRun,
}

type secretGitHubAppFlags struct {
	appID                string
	appInstallationOwner string
	appInstallationID    string
	privateKeyFile       string
	baseURL              string
}

var secretGitHubAppArgs = secretGitHubAppFlags{}

func init() {
	createSecretGitHubAppCmd.Flags().StringVar(&secretGitHubAppArgs.appID, "app-id", "", "github app ID")
	createSecretGitHubAppCmd.Flags().StringVar(&secretGitHubAppArgs.appInstallationOwner, "app-installation-owner", "", "github app installation owner (user or organization)")
	createSecretGitHubAppCmd.Flags().StringVar(&secretGitHubAppArgs.appInstallationID, "app-installation-id", "", "github app installation ID")
	createSecretGitHubAppCmd.Flags().StringVar(&secretGitHubAppArgs.privateKeyFile, "app-private-key", "", "github app private key file path")
	createSecretGitHubAppCmd.Flags().StringVar(&secretGitHubAppArgs.baseURL, "app-base-url", "", "github app base URL")

	createSecretCmd.AddCommand(createSecretGitHubAppCmd)
}

func createSecretGitHubAppCmdRun(cmd *cobra.Command, args []string) error {
	if len(args) < 1 {
		return fmt.Errorf("name is required")
	}

	secretName := args[0]

	privateKey, err := os.ReadFile(secretGitHubAppArgs.privateKeyFile)
	if err != nil {
		return fmt.Errorf("unable to read private key file: %w", err)
	}

	opts := sourcesecret.Options{
		Name:                       secretName,
		Namespace:                  *kubeconfigArgs.Namespace,
		GitHubAppID:                secretGitHubAppArgs.appID,
		GitHubAppInstallationOwner: secretGitHubAppArgs.appInstallationOwner,
		GitHubAppInstallationID:    secretGitHubAppArgs.appInstallationID,
		GitHubAppPrivateKey:        string(privateKey),
		GitHubAppBaseURL:           secretGitHubAppArgs.baseURL,
	}

	secret, err := sourcesecret.GenerateGitHubApp(opts)
	if err != nil {
		return err
	}

	if createArgs.export {
		rootCmd.Println(secret.Content)
		return nil
	}

	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
	defer cancel()
	kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
	if err != nil {
		return err
	}
	var s corev1.Secret
	if err := yaml.Unmarshal([]byte(secret.Content), &s); err != nil {
		return err
	}
	if err := upsertSecret(ctx, kubeClient, s); err != nil {
		return err
	}

	logger.Actionf("githubapp secret '%s' created in '%s' namespace", secretName, *kubeconfigArgs.Namespace)
	return nil
}
