package vcs

import (
	"net/url"
	"strings"

	"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
)

// ErrUnsupportedHost is returned by ParseRepoRef when the remote points at a
// host other than the one the v1 Gitea provider serves.
var ErrUnsupportedHost = errs.New(errs.CodeInvalidInput, "vcs: unsupported git host for PR operations")

// ParseRepoRef derives owner/name from a workspace GitRemoteURL. It accepts:
//
//	https://host/owner/name(.git)
//	http://host/owner/name(.git)
//	ssh://git@host/owner/name(.git)
//	git@host:owner/name(.git)
//
// wantHost is the bare host the provider serves (e.g. "git.jerryyan.net"); when
// non-empty the remote host must match it (port stripped, case-insensitive),
// otherwise ErrUnsupportedHost is returned. wantHost empty skips the host
// check (host-agnostic parse, used in tests).
func ParseRepoRef(remoteURL, wantHost string) (RepoRef, error) {
	remoteURL = strings.TrimSpace(remoteURL)
	if remoteURL == "" {
		return RepoRef{}, errs.New(errs.CodeInvalidInput, "vcs: empty remote url")
	}

	host, path, err := splitHostPath(remoteURL)
	if err != nil {
		return RepoRef{}, err
	}
	if wantHost != "" && !sameHost(host, wantHost) {
		return RepoRef{}, ErrUnsupportedHost
	}

	owner, name, err := splitOwnerName(path)
	if err != nil {
		return RepoRef{}, err
	}
	return RepoRef{Owner: owner, Name: name}, nil
}

// splitHostPath extracts the bare host and the repo path from a remote URL,
// handling both scheme URLs and the scp-like git@host:owner/name form.
func splitHostPath(remoteURL string) (host, path string, err error) {
	// scp-like: git@host:owner/name.git (no scheme, single colon before path)
	if !strings.Contains(remoteURL, "://") && strings.Contains(remoteURL, "@") && strings.Contains(remoteURL, ":") {
		at := strings.Index(remoteURL, "@")
		rest := remoteURL[at+1:]
		colon := strings.Index(rest, ":")
		if colon < 0 {
			return "", "", errs.New(errs.CodeInvalidInput, "vcs: malformed scp remote url")
		}
		return rest[:colon], rest[colon+1:], nil
	}

	u, perr := url.Parse(remoteURL)
	if perr != nil || u.Host == "" {
		return "", "", errs.Wrap(perr, errs.CodeInvalidInput, "vcs: cannot parse remote url")
	}
	return u.Hostname(), u.Path, nil
}

// splitOwnerName turns "/owner/name.git" or "owner/name.git" into (owner, name).
func splitOwnerName(path string) (owner, name string, err error) {
	path = strings.Trim(path, "/")
	path = strings.TrimSuffix(path, ".git")
	parts := strings.Split(path, "/")
	if len(parts) < 2 || parts[0] == "" || parts[len(parts)-1] == "" {
		return "", "", errs.New(errs.CodeInvalidInput, "vcs: remote url must contain owner/name")
	}
	// Gitea repos are owner/name; deeper paths are not valid repo refs.
	return parts[0], parts[len(parts)-1], nil
}

// sameHost compares hosts case-insensitively, ignoring any :port suffix.
func sameHost(a, b string) bool {
	return strings.EqualFold(stripPort(a), stripPort(b))
}

func stripPort(h string) string {
	if i := strings.LastIndex(h, ":"); i >= 0 {
		return h[:i]
	}
	return h
}
