package mtls

import (
	"crypto/tls"
	"net/http"
	"strings"
)

// SpiffeIDFromState extracts the SPIFFE ID (a spiffe:// URI SAN) from the
// VERIFIED peer certificate. Identity comes from the cryptographically
// proven cert — never from a client-controlled header.
func SpiffeIDFromState(cs *tls.ConnectionState) (string, bool) {
	if cs == nil || len(cs.PeerCertificates) == 0 {
		return "", false
	}
	for _, u := range cs.PeerCertificates[0].URIs {
		if u.Scheme == "spiffe" {
			return u.String(), true
		}
	}
	return "", false
}

// Policy maps a path prefix to the set of SPIFFE IDs allowed to call it.
// "*" allows any authenticated identity.
type Policy map[string][]string

// Allow reports whether identity may access path.
func (p Policy) Allow(path, identity string) bool {
	best, bestLen, found := []string(nil), -1, false
	for prefix, ids := range p {
		if strings.HasPrefix(path, prefix) && len(prefix) > bestLen {
			best, bestLen, found = ids, len(prefix), true
		}
	}
	if !found {
		return false // default deny
	}
	for _, id := range best {
		if id == "*" || id == identity {
			return true
		}
	}
	return false
}

// Authorize is HTTP middleware enforcing mTLS identity + policy. Identity
// is taken from the verified client cert (r.TLS); a request with no
// identity is 401, an unauthorized identity is 403.
func Authorize(p Policy, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id, ok := SpiffeIDFromState(r.TLS)
		if !ok {
			http.Error(w, "no client identity", http.StatusUnauthorized)
			return
		}
		if !p.Allow(r.URL.Path, id) {
			http.Error(w, "forbidden for "+id, http.StatusForbidden)
			return
		}
		r.Header.Set("X-Spiffe-Id", id) // pass identity downstream (trusted: we set it)
		next.ServeHTTP(w, r)
	})
}
