package runner

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

// FixedProviderTransportOptions describes one immutable authenticated provider
// endpoint. The Transport override is a test seam; production callers leave it
// nil so every connection uses the guarded provider dial path.
type FixedProviderTransportOptions struct {
	Endpoint    string
	Auth        AuthSpec
	Credentials Credentials
	Transport   http.RoundTripper
}

// NewFixedProviderTransport returns a RoundTripper that only sends requests to
// one exact HTTPS endpoint and applies the validated connection credentials
// after that endpoint check. It deliberately does not expose an unguarded
// http.Client: protocol runtimes layer their own timeout and response policy
// over this fixed destination/authentication seam.
func NewFixedProviderTransport(
	options FixedProviderTransportOptions,
) (http.RoundTripper, error) {
	endpoint, err := parseFixedProviderEndpoint(options.Endpoint)
	if err != nil {
		return nil, Errorf(
			ErrorValidation,
			"provider endpoint must be one fixed absolute HTTPS URL",
			nil,
		)
	}
	auth := cloneAuthSpec(options.Auth)
	credentials := options.Credentials.Clone()
	if err := auth.Validate(credentials); err != nil {
		return nil, Errorf(
			ErrorConnectionUnavailable,
			"provider credentials do not satisfy the authentication contract",
			nil,
		)
	}
	base := options.Transport
	if base == nil {
		base = guardedProviderTransport()
	}
	return &fixedProviderTransport{
		endpoint:    *endpoint,
		auth:        auth,
		credentials: credentials,
		base:        base,
	}, nil
}

type fixedProviderTransport struct {
	endpoint    url.URL
	auth        AuthSpec
	credentials Credentials
	base        http.RoundTripper
}

func (transport *fixedProviderTransport) RoundTrip(
	request *http.Request,
) (*http.Response, error) {
	if request == nil || request.URL == nil ||
		!sameFixedProviderEndpoint(&transport.endpoint, request.URL) {
		return nil, Errorf(
			ErrorValidation,
			"provider request escaped its fixed endpoint",
			nil,
		)
	}
	cloned := request.Clone(request.Context())
	cloned.Header = request.Header.Clone()
	cloned.Host = ""
	if err := transport.auth.applyAuthentication(
		cloned, transport.credentials,
	); err != nil {
		return nil, Errorf(
			ErrorConnectionUnavailable,
			"provider credentials could not be applied",
			nil,
		)
	}
	return transport.base.RoundTrip(cloned)
}

func (transport *fixedProviderTransport) CloseIdleConnections() {
	if closer, ok := transport.base.(interface{ CloseIdleConnections() }); ok {
		closer.CloseIdleConnections()
	}
}

func parseFixedProviderEndpoint(raw string) (*url.URL, error) {
	endpoint, err := url.Parse(strings.TrimSpace(raw))
	if err != nil ||
		endpoint.Scheme != "https" ||
		endpoint.Host == "" ||
		endpoint.User != nil ||
		endpoint.Opaque != "" ||
		endpoint.RawQuery != "" ||
		endpoint.ForceQuery ||
		endpoint.Fragment != "" ||
		endpoint.RawFragment != "" {
		return nil, Errorf(ErrorValidation, "provider endpoint is invalid", nil)
	}
	return endpoint, nil
}

func sameFixedProviderEndpoint(expected, actual *url.URL) bool {
	if expected == nil || actual == nil ||
		actual.User != nil ||
		actual.Opaque != "" ||
		actual.RawQuery != "" ||
		actual.ForceQuery ||
		actual.Fragment != "" ||
		actual.RawFragment != "" {
		return false
	}
	return strings.EqualFold(expected.Scheme, actual.Scheme) &&
		strings.EqualFold(expected.Host, actual.Host) &&
		expected.EscapedPath() == actual.EscapedPath()
}

func cloneAuthSpec(spec AuthSpec) AuthSpec {
	cloned := spec
	cloned.Fields = append([]CredentialField(nil), spec.Fields...)
	return cloned
}
