package signature

import (
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"strconv"
	"time"
)

type Validator struct {
	conf       SignatureConf
	nonceStore NonceStore
}

func NewValidator(conf SignatureConf, store NonceStore) *Validator {
	return &Validator{
		conf:       conf,
		nonceStore: store,
	}
}

func (v *Validator) windowSeconds() int64 {
	if v.conf.ValidWindowSeconds <= 0 {
		return 300
	}
	return v.conf.ValidWindowSeconds
}

func (v *Validator) Validate(ctx context.Context, appId, timestamp, nonce, signature, stringToSign string) error {
	secret, ok := v.conf.AppSecrets[appId]
	if !ok {
		return ErrSignatureMissing
	}

	ts, err := strconv.ParseInt(timestamp, 10, 64)
	if err != nil {
		return ErrSignatureExpired
	}
	diff := time.Now().Unix() - ts
	if diff < 0 {
		diff = -diff
	}
	if diff > v.windowSeconds() {
		return ErrSignatureExpired
	}

	replayed, err := v.nonceStore.SetIfNotExists(ctx, appId, nonce, v.windowSeconds()+60)
	if err == nil && replayed {
		return ErrSignatureReplay
	}

	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(stringToSign))
	expected := hex.EncodeToString(mac.Sum(nil))
	if !hmac.Equal([]byte(expected), []byte(signature)) {
		return ErrSignatureInvalid
	}
	return nil
}
