package core

import (
	"fmt"
	"net/url"
	"strings"
)

// RepoRef identifies a repository by its owner (without the leading '~') and
// name. Both fields should be validated with ValidOwner / ValidRepoName before
// they are used to build a filesystem path.
type RepoRef struct {
	Owner string
	Name  string
}

// CompareSpec is a parsed "base..head" or "base...head" comparison request.
// ThreeDot selects merge-base (symmetric-difference) semantics, matching git's
// "base...head"; when false the comparison is the plain "base..head" range.
type CompareSpec struct {
	Base     string
	Head     string
	ThreeDot bool
}

// ParseCompareSpec parses the compare wildcard from a URL path segment into a
// CompareSpec. The grammar is:
//
//	base "..." head   -> ThreeDot = true   (merge-base / symmetric diff)
//	base ".."  head   -> ThreeDot = false  (direct range)
//
// The three-dot form is tried first, because "..." contains "..". Each side is
// then url.PathUnescape'd and validated with ValidRef. Both sides must be
// non-empty and valid or ErrBadRef (wrapped with detail) is returned.
//
// Stripping a trailing ".patch" (the raw-diff escape hatch) is the caller's
// responsibility and is intentionally not handled here.
func ParseCompareSpec(raw string) (CompareSpec, error) {
	var base, head string
	var threeDot bool

	switch {
	case strings.Contains(raw, "..."):
		i := strings.Index(raw, "...")
		threeDot = true
		base, head = raw[:i], raw[i+3:]
	case strings.Contains(raw, ".."):
		i := strings.Index(raw, "..")
		threeDot = false
		base, head = raw[:i], raw[i+2:]
	default:
		return CompareSpec{}, fmt.Errorf("%w: missing '..' or '...' separator in %q", ErrBadRef, raw)
	}

	b, err := url.PathUnescape(base)
	if err != nil {
		return CompareSpec{}, fmt.Errorf("%w: bad percent-encoding in base: %v", ErrBadRef, err)
	}
	h, err := url.PathUnescape(head)
	if err != nil {
		return CompareSpec{}, fmt.Errorf("%w: bad percent-encoding in head: %v", ErrBadRef, err)
	}

	if !ValidRef(b) {
		return CompareSpec{}, fmt.Errorf("%w: invalid base ref %q", ErrBadRef, b)
	}
	if !ValidRef(h) {
		return CompareSpec{}, fmt.Errorf("%w: invalid head ref %q", ErrBadRef, h)
	}

	return CompareSpec{Base: b, Head: h, ThreeDot: threeDot}, nil
}
