package apikey

import (
	"context"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"strings"
	"time"
)

var (
	ErrInvalid = errors.New("invalid API key")
	ErrStore   = errors.New("API key store unavailable")
)

type Record struct {
	ID                  string
	TenantID            *string
	Name                string
	KeyPrefix           string
	KeyHash             []byte
	Scopes              []string
	Enabled             bool
	RequestsPerMinute   int
	MonthlyRequestQuota int64
	MonthlyTokenQuota   int64
	ExpiresAt           *time.Time
	LastUsedAt          *time.Time
	CreatedAt           time.Time
}

type Principal struct {
	APIKeyID            string   `json:"api_key_id"`
	TenantID            *string  `json:"tenant_id,omitempty"`
	Scopes              []string `json:"scopes"`
	RequestsPerMinute   int      `json:"requests_per_minute"`
	MonthlyRequestQuota int64    `json:"monthly_request_quota"`
	MonthlyTokenQuota   int64    `json:"monthly_token_quota"`
}

func Generate() (secret, prefix string, hash []byte, err error) {
	random := make([]byte, 32)
	if _, err = rand.Read(random); err != nil {
		return "", "", nil, err
	}
	secret = "gw_" + base64.RawURLEncoding.EncodeToString(random)
	prefix = secret[:16]
	digest := sha256.Sum256([]byte(secret))
	return secret, prefix, digest[:], nil
}

func Digest(secret string) ([]byte, string) {
	digest := sha256.Sum256([]byte(strings.TrimSpace(secret)))
	return digest[:], hex.EncodeToString(digest[:])
}

func HasScope(scopes []string, required string) bool {
	for _, scope := range scopes {
		if scope == "*" || scope == required {
			return true
		}
	}
	return false
}

type KeyAuthenticator interface {
	Authenticate(context.Context, string) error
}

type PrincipalAuthenticator interface {
	AuthenticatePrincipal(context.Context, string) (Principal, error)
}
