package git

import (
	"context"
	"errors"
	"fmt"
	"net/url"
	"path"
	"strings"

	"github.com/caarlos0/log"
	"github.com/goreleaser/goreleaser/v2/pkg/config"
)

// ExtractRepoFromConfig gets the repo name from the Git config.
func ExtractRepoFromConfig(ctx context.Context) (result config.Repo, err error) {
	if !IsRepo(ctx) {
		return result, errors.New("current folder is not a git repository")
	}
	out, err := Clean(Run(ctx, "ls-remote", "--get-url"))
	if err != nil {
		return result, errors.New("no remote configured to list refs from")
	}
	// This is a relative remote URL and requires some additional processing
	if out == "." {
		return extractRelativeRepoFromConfig(ctx)
	}
	return ExtractRepoFromURL(out)
}

func extractRelativeRepoFromConfig(ctx context.Context) (result config.Repo, err error) {
	out, err := Clean(Run(ctx, "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"))
	if err != nil {
		return result, fmt.Errorf("unable to get upstream while qualifying relative remote: %w", err)
	}
	if out == "" {
		return result, errors.New("unable to get upstream while qualifying relative remote: empty output")
	}
	out, err = Clean(Run(ctx, "config", "--get", fmt.Sprintf("branch.%s.remote", out)))
	if err != nil {
		return result, fmt.Errorf("unable to get upstream's remote while qualifying relative remote: %w", err)
	}
	if out == "" {
		return result, errors.New("unable to get upstream's remote while qualifying relative remote: empty output")
	}
	out, err = Clean(Run(ctx, "ls-remote", "--get-url", out))
	if err != nil {
		return result, fmt.Errorf("unable to get upstream while qualifying relative remote: %w", err)
	}
	return ExtractRepoFromURL(out)
}

func ExtractRepoFromURL(rawurl string) (config.Repo, error) {
	// removes the .git suffix and any new lines
	s := strings.TrimSuffix(strings.TrimSpace(rawurl), ".git")

	// if the URL contains a :, indicating a SSH config,
	// remove all chars until it, including itself
	// on HTTP and HTTPS URLs it will remove the http(s): prefix,
	// which is ok. On SSH URLs the whole user@server will be removed,
	// which is required.

	// If the url contains more than 1 ':' character, assume we are doing an
	// http URL with a username/password in it, and normalize the URL.
	// Gitlab-CI uses this type of URL
	if strings.Count(s, ":") == 1 {
		s = s[strings.LastIndex(s, ":")+1:]
	}

	// now we can parse it with net/url
	u, err := url.Parse(s)
	if err != nil {
		return config.Repo{
			RawURL: rawurl,
		}, err
	}

	// split the parsed url path by /, the last parts should be the owner and name
	ss := strings.Split(strings.TrimPrefix(u.Path, "/"), "/")

	// if empty, returns an error
	if len(ss) == 0 || ss[0] == "" {
		return config.Repo{
			RawURL: rawurl,
		}, fmt.Errorf("unsupported repository URL: %s", rawurl)
	}

	// if less than 2 parts, its likely not a valid repository, but we'll allow it.
	if len(ss) < 2 {
		return config.Repo{
			RawURL: rawurl,
			Owner:  ss[0],
		}, nil
	}
	repo := config.Repo{
		RawURL: rawurl,
		Owner:  path.Join(ss[:len(ss)-1]...),
		Name:   ss[len(ss)-1],
	}
	log.WithField("owner", repo.Owner).WithField("name", repo.Name).Debugf("parsed url")
	return repo, nil
}
