package webshared

import (
	"crypto/sha256"
	"net/http"
	"time"

	"git.mstar.dev/mstar/goutils/maputils"

	"git.mstar.dev/mstar/linstrom/config"
)

// No init needed, zero value is good

var RequestClient http.Client

const xRandomHeader = "X-Auth-Random"

// Sign a given outbound request for authorized fetch.
// At the end, the Signature header will have the signature needed,
// nothing else is modified.
// If the request is POST, the postBody must contain the raw body of
// the request and the Digest header will also be added
func SignRequest(r *http.Request, keyId string, privateKeyBytes, postBody []byte) error {
	method := r.Method
	headers := r.Header
	var nowString string
	if dateString := headers.Get("Date"); dateString != "" {
		nowString = dateString
	} else {
		nowString = time.Now().Format("Mon, 02 Jan 2006 15:04:05 MST")
		headers.Set("Date", nowString)
	}
	var host string
	if hostString := headers.Get("Host"); hostString != "" {
		host = hostString
	} else {
		host = config.GlobalConfig.General.GetFullDomain()
		headers.Set("Date", host)
	}
	applyBodyHash(headers, postBody)
	mappedHeaders := maputils.MapSameKeys(headers, func(k string, v []string) string {
		if len(v) > 0 {
			return v[0]
		} else {
			return ""
		}
	})
	var signedString string
	if config.GlobalConfig.Experimental.UseEd25519Keys {
		tmp, err := CreateSignatureED(method, r.URL.RawPath, mappedHeaders, privateKeyBytes)
		if err != nil {
			return err
		}
		signedString = tmp
	} else {
		tmp, err := CreateSignatureRSA(method, r.URL.RawPath, mappedHeaders, privateKeyBytes)
		if err != nil {
			return err
		}
		signedString = tmp
	}
	signature := CreateSignatureHeaderContent(
		keyId,
		signedString,
		maputils.KeysFromMap(mappedHeaders)...,
	)
	headers.Set("Signature", signature)
	return nil
}

func applyBodyHash(headers http.Header, body []byte) error {
	if body == nil {
		return nil
	}
	hash := sha256.Sum256(body)
	headers.Set("Digest", string(hash[:]))
	return nil
}
