package httpauth

import (
	"encoding/base64"
	"net/http"
	"net/url"
	"strings"

	"lol.mleku.dev/chk"
	"next.orly.dev/pkg/encoders/event"
	"next.orly.dev/pkg/encoders/kind"
	"next.orly.dev/pkg/encoders/tag"
	"next.orly.dev/pkg/encoders/timestamp"
	"next.orly.dev/pkg/interfaces/signer"
)

const (
	HeaderKey   = "Authorization"
	NIP98Prefix = "Nostr"
)

// MakeNIP98Event creates a new NIP-98 event. If expiry is given, method is
// ignored; otherwise either option is the same.
func MakeNIP98Event(u, method, hash string, expiry int64) (ev *event.E) {
	var t []*tag.T
	t = append(t, tag.NewFromAny("u", u))
	if expiry > 0 {
		t = append(
			t,
			tag.NewFromAny("expiration", timestamp.FromUnix(expiry).String()),
		)
	} else {
		t = append(
			t,
			tag.NewFromAny("method", strings.ToUpper(method)),
		)
	}
	if hash != "" {
		t = append(t, tag.NewFromAny("payload", hash))
	}
	ev = &event.E{
		CreatedAt: timestamp.Now().V,
		Kind:      kind.HTTPAuth.K,
		Tags:      tag.NewS(t...),
	}
	return
}

func CreateNIP98Blob(
	ur, method, hash string, expiry int64, sign signer.I,
) (blob string, err error) {
	ev := MakeNIP98Event(ur, method, hash, expiry)
	if err = ev.Sign(sign); chk.E(err) {
		return
	}
	// log.T.F("nip-98 http auth event:\n%s\n", ev.SerializeIndented())
	blob = base64.URLEncoding.EncodeToString(ev.Serialize())
	return
}

// AddNIP98Header creates a NIP-98 http auth event and adds the standard header to a provided
// http.Request.
func AddNIP98Header(
	r *http.Request, ur *url.URL, method, hash string,
	sign signer.I, expiry int64,
) (err error) {
	var b64 string
	if b64, err = CreateNIP98Blob(
		ur.String(), method, hash, expiry, sign,
	); chk.E(err) {
		return
	}
	r.Header.Add(HeaderKey, "Nostr "+b64)
	return
}
