/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/

// oci-pin generates Go code and shasum files for OCI images.
package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"strings"

	"github.com/spf13/cobra"
)

func main() {
	if err := execute(); err != nil {
		os.Exit(1)
	}
}

func execute() error {
	rootCmd := newRootCmd()
	ctx, cancel := signalContext(context.Background(), os.Interrupt)
	defer cancel()
	return rootCmd.ExecuteContext(ctx)
}

func newRootCmd() *cobra.Command {
	rootCmd := &cobra.Command{
		Use:              "oci-pin",
		Short:            "Generate pinning artifacts for OCI images.",
		Long:             "Generate pinning artifacts (Go code, shasum files) for OCI images.",
		PersistentPreRun: preRunRoot,
	}

	rootCmd.SetOut(os.Stdout)

	rootCmd.PersistentFlags().Bool("verbose", false, "Enable verbose output.")

	rootCmd.AddCommand(newCodegenCmd())
	rootCmd.AddCommand(newSumCmd())
	rootCmd.AddCommand(newMergeCmd())

	return rootCmd
}

// signalContext returns a context that is canceled on the handed signal.
// The signal isn't watched after its first occurrence. Call the cancel
// function to ensure the internal goroutine is stopped and the signal isn't
// watched any longer.
func signalContext(ctx context.Context, sig os.Signal) (context.Context, context.CancelFunc) {
	sigCtx, stop := signal.NotifyContext(ctx, sig)
	done := make(chan struct{}, 1)
	stopDone := make(chan struct{}, 1)

	go func() {
		defer func() { stopDone <- struct{}{} }()
		defer stop()
		select {
		case <-sigCtx.Done():
			fmt.Println(" Signal caught. Press ctrl+c again to terminate the program immediately.")
		case <-done:
		}
	}()

	cancelFunc := func() {
		done <- struct{}{}
		<-stopDone
	}

	return sigCtx, cancelFunc
}

func preRunRoot(cmd *cobra.Command, _ []string) {
	cmd.SilenceUsage = true
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func splitRepoTag(ref string) (registry, prefix, name, tag string, err error) {
	// last colon is separator between name and tag
	tagSep := strings.LastIndexByte(ref, ':')
	if tagSep == -1 {
		return "", "", "", "", fmt.Errorf("invalid OCI image reference %q: missing tag", ref)
	}
	tag = ref[tagSep+1:]
	base := ref[:tagSep]

	// first slash is separator between registry and full name
	registrySep := strings.IndexByte(base, '/')
	if registrySep == -1 {
		return "", "", "", "", fmt.Errorf("invalid OCI image reference %q: missing registry", ref)
	}

	registry = base[:registrySep]
	fullName := base[registrySep+1:]

	// last slash is separator between prefix and short name
	nameSep := strings.LastIndexByte(fullName, '/')
	if nameSep == -1 {
		name = fullName
	} else {
		prefix = fullName[:nameSep]
		name = fullName[nameSep+1:]
	}
	return
}