package gitutil

import (
	"regexp"
	"strings"

	"github.com/containerd/containerd/errdefs"
)

// GitRef represents a git ref.
//
// Examples:
// - "https://github.com/foo/bar.git#baz/qux:quux/quuz" is parsed into:
//   {Remote: "https://github.com/foo/bar.git", ShortName: "bar", Commit:"baz/qux", SubDir: "quux/quuz"}.
type GitRef struct {
	// Remote is the remote repository path.
	Remote string

	// ShortName is the directory name of the repo.
	// e.g., "bar" for "https://github.com/foo/bar.git"
	ShortName string

	// Commit is a commit hash, a tag, or branch name.
	// Commit is optional.
	Commit string

	// SubDir is a directory path inside the repo.
	// SubDir is optional.
	SubDir string

	// IndistinguishableFromLocal is true for a ref that is indistinguishable from a local file path,
	// e.g., "github.com/foo/bar".
	//
	// Deprecated.
	// Instead, use a distinguishable form such as "https://github.com/foo/bar.git".
	//
	// The dockerfile frontend still accepts this form only for build contexts.
	IndistinguishableFromLocal bool

	// UnencryptedTCP is true for a ref that needs an unencrypted TCP connection,
	// e.g., "git://..." and "http://..." .
	//
	// Discouraged, although not deprecated.
	// Instead, consider using an encrypted TCP connection such as "git@github.com/foo/bar.git" or "https://github.com/foo/bar.git".
	UnencryptedTCP bool
}

// var gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)

// ParseGitRef parses a git ref.
func ParseGitRef(ref string) (*GitRef, error) {
	res := &GitRef{}

	if strings.HasPrefix(ref, "github.com/") {
		res.IndistinguishableFromLocal = true // Deprecated
	} else {
		_, proto := ParseProtocol(ref)
		switch proto {
		case UnknownProtocol:
			return nil, errdefs.ErrInvalidArgument
		}
		switch proto {
		case HTTPProtocol, GitProtocol:
			res.UnencryptedTCP = true // Discouraged, but not deprecated
		}
		switch proto {
		// An HTTP(S) URL is considered to be a valid git ref only when it has the ".git[...]" suffix.
		case HTTPProtocol, HTTPSProtocol:
			var gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
			if !gitURLPathWithFragmentSuffix.MatchString(ref) {
				return nil, errdefs.ErrInvalidArgument
			}
		}
	}

	refSplitBySharp := strings.SplitN(ref, "#", 2)
	res.Remote = refSplitBySharp[0]
	if len(res.Remote) == 0 {
		return res, errdefs.ErrInvalidArgument
	}

	if len(refSplitBySharp) > 1 {
		refSplitBySharpSplitByColon := strings.SplitN(refSplitBySharp[1], ":", 2)
		res.Commit = refSplitBySharpSplitByColon[0]
		if len(res.Commit) == 0 {
			return res, errdefs.ErrInvalidArgument
		}
		if len(refSplitBySharpSplitByColon) > 1 {
			res.SubDir = refSplitBySharpSplitByColon[1]
		}
	}
	repoSplitBySlash := strings.Split(res.Remote, "/")
	res.ShortName = strings.TrimSuffix(repoSplitBySlash[len(repoSplitBySlash)-1], ".git")
	return res, nil
}