package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	"github.com/openeuler/Conch/internal/image/conchbuild/buildahcli"
)

type pushOptions struct {
	localImage  string
	remoteImage string
	tlsVerify   bool
}

func printPushHelp(out io.Writer) {
	fmt.Fprintln(out, "Usage:")
	fmt.Fprintln(out, "  conch push [options] <local-image> <remote-image>")
	fmt.Fprintln(out, "")
	fmt.Fprintln(out, "Description:")
	fmt.Fprintln(out, "  Push a Conch OCI index with `buildah manifest push --all`.")
	fmt.Fprintln(out, "  Registry authentication is handled by buildah, for example `buildah login <registry>`.")
	fmt.Fprintln(out, "")
	fmt.Fprintln(out, "Options:")
	fmt.Fprintln(out, "  --plain-http")
	fmt.Fprintln(out, "        allow plain HTTP / disable TLS verification for the destination registry")
	fmt.Fprintln(out, "  --tls-verify bool")
	fmt.Fprintln(out, "        verify TLS certificates for the destination registry (default: true)")
	fmt.Fprintln(out, "")
	fmt.Fprintln(out, "Example:")
	fmt.Fprintln(out, "  conch push localhost/demo-index:latest hub.oepkgs.net/conch/demo-index:latest")
	fmt.Fprintln(out, "  conch push --plain-http localhost/demo-index:latest conch.example.com/conch/demo-index:latest")
}

func runPush(ctx context.Context, args []string) error {
	opts, err := parsePushArgs(args)
	if err != nil {
		return err
	}

	cmdArgs := buildPushCommandArgs(opts)
	cmd := exec.CommandContext(ctx, buildahcli.Bin(), cmdArgs...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("conch push: buildah %s: %w", strings.Join(cmdArgs, " "), err)
	}

	fmt.Printf("Pushed image: %s -> %s\n", opts.localImage, opts.remoteImage)
	return nil
}

func parsePushArgs(args []string) (pushOptions, error) {
	opts := pushOptions{tlsVerify: true}
	images := make([]string, 0, 2)

	for i := 0; i < len(args); i++ {
		arg := args[i]
		switch {
		case arg == "--plain-http":
			opts.tlsVerify = false
		case arg == "--tls-verify":
			if i+1 >= len(args) {
				return pushOptions{}, fmt.Errorf("conch push: missing value for %s", arg)
			}
			value, err := parseBoolFlagValue(args[i+1], arg)
			if err != nil {
				return pushOptions{}, err
			}
			opts.tlsVerify = value
			i++
		case strings.HasPrefix(arg, "--tls-verify="):
			value, err := parseBoolFlagValue(strings.TrimPrefix(arg, "--tls-verify="), "--tls-verify")
			if err != nil {
				return pushOptions{}, err
			}
			opts.tlsVerify = value
		case strings.HasPrefix(arg, "-"):
			return pushOptions{}, fmt.Errorf("conch push: unknown option %s", arg)
		default:
			images = append(images, arg)
		}
	}

	if len(images) != 2 {
		return pushOptions{}, fmt.Errorf("conch push: exactly two image names are required: <local-image> <remote-image>")
	}
	opts.localImage = images[0]
	opts.remoteImage = images[1]
	return opts, nil
}

func parseBoolFlagValue(value, flagName string) (bool, error) {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case "1", "t", "true", "y", "yes":
		return true, nil
	case "0", "f", "false", "n", "no":
		return false, nil
	default:
		return false, fmt.Errorf("conch push: invalid %s value %q", flagName, value)
	}
}

func buildPushCommandArgs(opts pushOptions) []string {
	args := []string{"manifest", "push", "--all"}
	if !opts.tlsVerify {
		args = append(args, "--tls-verify=false")
	}
	args = append(args, opts.localImage, ensureDockerTransport(opts.remoteImage))
	return args
}

func ensureDockerTransport(imageName string) string {
	if strings.Contains(imageName, "://") {
		return imageName
	}
	return "docker://" + imageName
}