package httprange

import (
	
	
	
	
	

	
)

const (
	httpHeaderETag         = "Etag"
	httpHeaderLastModified = "Last-Modified"
	httpHeaderDate         = "Date"
	weakETagPrefix         = "W/"
)

// HTTPMetadataProvider provides representation metadata for a resource.
type HTTPMetadataProvider interface {
	Provide() *HTTPMetadata
}

// HTTPMetadata contains representation metadata for a resource.
type HTTPMetadata struct {
	// ContentLength is the complete length of the representation in bytes.
	ContentLength int64

	// ETag is the current representation entity tag.
	ETag string

	// LastModified is the last modification time.
	LastModified time.Time

	// Date is the server date when the metadata was retrieved.
	Date time.Time

	// AcceptRanges contains advertised Accept-Ranges header values.
	AcceptRanges []string
}

// Provide implements the [HTTPMetadataProvider] interface.
func ( *HTTPMetadata) () *HTTPMetadata {
	return 
}

// HTTPMetadataExtractor extracts representation metadata for a resource.
type HTTPMetadataExtractor interface {
	// Extract retrieves metadata for the resource. It returns the metadata
	// or an error if the extraction fails.
	Extract(context.Context) (HTTPMetadataProvider, error)
}

// HTTPResponseMetadataExtractor implements [HTTPMetadataExtractor] by
// performing an HTTP HEAD request to retrieve representation metadata.
type HTTPResponseMetadataExtractor struct {
	// Request is a builder for HTTP HEAD request that is used to retrieve
	// representation metadata.
	Request HTTPRequestBuilder

	// Client is the HTTP client instance to use.
	Client httpclient.Client
}

// Extract performs a HEAD request and extracts metadata from the response.
func ( *HTTPResponseMetadataExtractor) ( context.Context) (HTTPMetadataProvider, error) {
	,  := .Request.Build()
	if  != nil {
		return nil, 
	}

	,  := .Client.Do()
	if  != nil {
		return nil, 
	}

	 := func() { _ = .Body.Close() }
	 := context.AfterFunc(, )
	defer func() {
		if () {
			()
		}
	}()

	,  := extractHTTPResponseMetadata()
	if  != nil {
		return nil, &HTTPResponseError{
			Response: ,
			cause:    ,
		}
	}
	return , nil
}

func ( *http.Response) (*HTTPMetadata, error) {
	if .StatusCode/100 != 2 {
		return nil, &UnexpectedStatusCodeError{
			Status:     .Status,
			StatusCode: .StatusCode,
		}
	}

	,  := parseETagOrZero(.Header)
	if  != nil {
		return nil, 
	}

	,  := parseTimeHeaderOrZero(.Header, httpHeaderLastModified)
	if  != nil {
		return nil, 
	}

	,  := parseTimeHeaderOrZero(.Header, httpHeaderDate)
	if  != nil {
		return nil, 
	}

	return &HTTPMetadata{
		ContentLength: .ContentLength,
		ETag:          ,
		LastModified:  ,
		Date:          ,
		AcceptRanges:  .Header.Values(httpHeaderAcceptRanges),
	}, nil
}

func ( http.Header) (string, error) {
	 := .Get(httpHeaderETag)
	if  == "" {
		return "", nil
	}
	if !isValidETag() {
		return "", fmt.Errorf(
			"httprange: invalid ETag header value %q",
			,
		)
	}
	return , nil
}

func ( string) bool {
	 = strings.TrimPrefix(, weakETagPrefix)
	if len() < 2 || [0] != '"' || [len()-1] != '"' {
		return false
	}
	for  := 1;  < len()-1; ++ {
		// VCHAR except double quotes, plus obs-text.
		if  := [];  <= 0x20 ||  == '"' ||  == 0x7F {
			return false
		}
	}
	return true
}

func ( http.Header,  string) (time.Time, error) {
	 := .Get()
	if  == "" {
		return time.Time{}, nil
	}
	,  := http.ParseTime()
	if  != nil {
		return time.Time{}, fmt.Errorf("httprange: parse %q header: %w", , )
	}
	return , nil
}