// Package repositoryroute owns AgentTeam synthetic repository route contracts that
// are shared by the edge classifier and the forwarding implementation.
package repositoryroutecontract

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

// RawRequest is the once-parsed contract for AgentTeam's synthetic raw-content
// compatibility route. AGS does not register this route itself.
type RawRequest struct {
	Owner      string
	Repository string
	Ref        string
	FilePath   string
	Capability string
}

// ParseRawRequest accepts only GET /{owner}/{repo}/raw/{ref}/{filePath}. Ref
// may contain an encoded slash; file path segments are decoded exactly once.
func ParseRawRequest(request *http.Request) (RawRequest, error) {
	if request == nil || request.URL == nil || request.Method != http.MethodGet {
		return RawRequest{}, fmt.Errorf("raw repository request requires GET")
	}
	escapedPath := request.URL.EscapedPath()
	if request.URL.RawPath != "" {
		decoded, err := url.PathUnescape(request.URL.RawPath)
		if err != nil || decoded != request.URL.Path {
			return RawRequest{}, fmt.Errorf("invalid escaped raw repository path")
		}
		escapedPath = request.URL.RawPath
	}
	if len(escapedPath) < 2 || escapedPath[0] != '/' || escapedPath[1] == '/' || strings.HasSuffix(escapedPath, "/") {
		return RawRequest{}, fmt.Errorf("raw repository path must be canonical")
	}
	parts := strings.Split(escapedPath[1:], "/")
	if len(parts) < 5 || parts[0] == "" || parts[1] == "" || parts[2] != "raw" || parts[3] == "" {
		return RawRequest{}, fmt.Errorf("raw repository path missing owner, repository, ref, or file path")
	}
	owner, err := url.PathUnescape(parts[0])
	if err != nil || !safePathAtom(owner) {
		return RawRequest{}, fmt.Errorf("invalid raw repository owner")
	}
	repository, err := url.PathUnescape(parts[1])
	if err != nil || !safePathAtom(repository) {
		return RawRequest{}, fmt.Errorf("invalid raw repository name")
	}
	ref, err := url.PathUnescape(parts[3])
	if err != nil || !safeRef(ref) {
		return RawRequest{}, fmt.Errorf("invalid raw repository ref")
	}
	fileParts := make([]string, 0, len(parts)-4)
	for _, part := range parts[4:] {
		if part == "" {
			return RawRequest{}, fmt.Errorf("invalid raw repository file path")
		}
		decoded, err := url.PathUnescape(part)
		if err != nil || !safePathAtom(decoded) {
			return RawRequest{}, fmt.Errorf("invalid raw repository file path")
		}
		fileParts = append(fileParts, decoded)
	}
	return RawRequest{
		Owner: owner, Repository: repository, Ref: ref,
		FilePath: strings.Join(fileParts, "/"), Capability: "repo:read",
	}, nil
}

func safeRef(value string) bool {
	if value == "" || hasControl(value) || strings.Contains(value, "\\") {
		return false
	}
	for _, segment := range strings.Split(value, "/") {
		if segment == "" || segment == "." || segment == ".." {
			return false
		}
	}
	return true
}

func safePathAtom(value string) bool {
	return value != "" && value != "." && value != ".." &&
		!strings.ContainsAny(value, "/\\") && !hasControl(value)
}

func hasControl(value string) bool {
	for _, char := range value {
		if char == 0 || char < ' ' || char == 0x7f {
			return true
		}
	}
	return false
}
