// Package docker provides commands that help configure a docker
// credential helper.
package docker

import (
	"fmt"
	"runtime"

	"github.com/MakeNowJust/heredoc/v2"
	"github.com/docker/docker-credential-helpers/credentials"
	"github.com/spf13/cobra"

	"gitlab.com/gitlab-org/cli/internal/cmdutils"
	"gitlab.com/gitlab-org/cli/internal/mcpannotations"
)

const (
	// helperFullName is the full name of the credential helper,
	// and is what Docker will look for in the user's PATH.
	helperFullName = "docker-credential-glab"
	// helperShortName is the short name of the credential,
	// and is what the Docker config will list in the credHelpers
	// configuration object.
	helperShortName = "glab"
)

// NewCmdConfigureDocker returns a command that configures the CLI
// as a Docker credential helper.
func NewCmdConfigureDocker(f cmdutils.Factory) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "configure-docker",
		Args:  cobra.ExactArgs(0),
		Short: "Register glab as a Docker credential helper.",
		Long: heredoc.Doc(`
		Configures Docker to use glab for authentication with GitLab
		container registries. This command runs only on Linux and macOS.

		After you run this command, Docker uses glab to obtain credentials
		when it pulls from or pushes to a GitLab container registry.
		`),
		Example: heredoc.Doc(`
			# Configure Docker to use glab for GitLab container registry authentication
			glab auth configure-docker
		`),
		Annotations: map[string]string{
			mcpannotations.Destructive: "true",
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			// TODO: The shell wrapper approach is only implemented for POSIX
			// compliant operating systems at the moment.
			// See https://gitlab.com/gitlab-org/cli/-/issues/7906 to track
			// the work on support for additional operating systems.
			if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
				return fmt.Errorf("operating system %q is not supported; "+
					"only Linux and MacOS (Darwin) are supported", runtime.GOOS)
			}

			io := f.IO()
			cfg := f.Config()
			return configureDocker(io, cfg)
		},
	}
	return cmd
}

// NewCmdCredentialHelper returns a command that handles Docker credential
// requests.
func NewCmdCredentialHelper(f cmdutils.Factory) *cobra.Command {
	cmd := &cobra.Command{
		Use:       "docker-helper",
		ValidArgs: []cobra.Completion{"store", "get", "erase"},
		Args: cobra.MatchAll(
			helperArgCheck,
			cobra.OnlyValidArgs,
		),
		Short: "A Docker credential helper for GitLab container registries.",
		Long: heredoc.Doc(`
		Responds to Docker credential helper requests for GitLab container
		registries. Docker invokes this command automatically.
		`),
		Example: heredoc.Doc(`
		# Docker invokes the helper automatically; supported actions are 'store', 'get', and 'erase'.
		# Retrieve the stored credentials for a registry:
		echo registry.gitlab.com | glab auth docker-helper get`),
		Annotations: map[string]string{
			mcpannotations.Destructive: "true",
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			apiClient, err := f.ApiClient("")
			if err != nil {
				return err
			}

			httpClient := apiClient.HTTPClient()

			credHelper := Helper{httpClient, f.Config()}

			action := args[0]
			return credentials.HandleCommand(&credHelper, action, f.IO().In, f.IO().StdOut)
		},
	}
	return cmd
}

func helperArgCheck(cmd *cobra.Command, args []string) error {
	validation := cobra.ExactArgs(1)
	if err := validation(cmd, args); err != nil {
		return fmt.Errorf("arg is missing - valid args: %s", cmd.ValidArgs)
	}
	return nil
}
