package mcp

import (
	"context"
	"net/http"

	"github.com/zeromicro/go-zero/rest/pathvar"
)

// RequestMetadata carries selected request-scoped values into MCP handlers.
type RequestMetadata struct {
	Headers map[string][]string
	Query   map[string][]string
	Path    map[string]string
}

type requestMetadataCtxKey struct{}

// RequestMetadataFromContext returns metadata extracted at the transport boundary.
func RequestMetadataFromContext(ctx context.Context) (RequestMetadata, bool) {
	metadata, ok := requestMetadataFromContext(ctx)
	if !ok {
		return RequestMetadata{}, false
	}

	return normalizeRequestMetadata(metadata), true
}

// HeaderFromContext returns the first header value for key.
func HeaderFromContext(ctx context.Context, key string) (string, bool) {
	metadata, ok := requestMetadataFromContext(ctx)
	if !ok {
		return "", false
	}

	vals := metadata.Headers[http.CanonicalHeaderKey(key)]
	if len(vals) == 0 {
		return "", false
	}

	return vals[0], true
}

// QueryFromContext returns the first query value for key.
func QueryFromContext(ctx context.Context, key string) (string, bool) {
	metadata, ok := requestMetadataFromContext(ctx)
	if !ok {
		return "", false
	}

	vals := metadata.Query[key]
	if len(vals) == 0 {
		return "", false
	}

	return vals[0], true
}

// PathFromContext returns the path variable value for key.
func PathFromContext(ctx context.Context, key string) (string, bool) {
	metadata, ok := requestMetadataFromContext(ctx)
	if !ok {
		return "", false
	}

	val, ok := metadata.Path[key]
	if !ok {
		return "", false
	}

	return val, true
}

func requestMetadataFromContext(ctx context.Context) (RequestMetadata, bool) {
	metadata, ok := ctx.Value(requestMetadataCtxKey{}).(RequestMetadata)
	if !ok {
		return RequestMetadata{}, false
	}

	return metadata, true
}

// DefaultRequestMetadataExtractor extracts headers, query values, and path variables.
func DefaultRequestMetadataExtractor(r *http.Request) RequestMetadata {
	metadata := RequestMetadata{
		Headers: make(map[string][]string, len(r.Header)),
		Query:   make(map[string][]string),
		Path:    clonePathVars(pathvar.Vars(r)),
	}

	for key, vals := range r.Header {
		metadata.Headers[http.CanonicalHeaderKey(key)] = append([]string(nil), vals...)
	}

	if r.URL != nil {
		for key, vals := range r.URL.Query() {
			metadata.Query[key] = append([]string(nil), vals...)
		}
	}

	return metadata
}

func normalizeRequestMetadata(metadata RequestMetadata) RequestMetadata {
	return RequestMetadata{
		Headers: cloneCanonicalHeaderValues(metadata.Headers),
		Query:   cloneHeaderValues(metadata.Query),
		Path:    clonePathVars(metadata.Path),
	}
}

func cloneHeaderValues(values map[string][]string) map[string][]string {
	if len(values) == 0 {
		return nil
	}

	cloned := make(map[string][]string, len(values))
	for key, vals := range values {
		cloned[key] = append([]string(nil), vals...)
	}

	return cloned
}

func cloneCanonicalHeaderValues(values map[string][]string) map[string][]string {
	if len(values) == 0 {
		return nil
	}

	cloned := make(map[string][]string, len(values))
	for key, vals := range values {
		canonical := http.CanonicalHeaderKey(key)
		cloned[canonical] = append(cloned[canonical], vals...)
	}

	return cloned
}

func clonePathVars(values map[string]string) map[string]string {
	if len(values) == 0 {
		return nil
	}

	cloned := make(map[string]string, len(values))
	for key, val := range values {
		cloned[key] = val
	}

	return cloned
}
