// Package license defines the Vantage licence payload and its offline
// verification.
//
// A licence is a signed blob (ECDSA P-384 with SHA-256). The server checks a
// signature, an expiry, a deployment mode and an instance ID, and asks nobody's
// permission. That buys air-gapped self-hosting and means no instance depends
// on the licensing service being reachable.
//
// It costs revocation: once issued, a licence is valid until it expires
// whatever the billing system later says. Self Hosted is sold annually only so
// that window is bounded.
package license

import "time"

const (
	TierFree         = "free"
	TierProfessional = "professional"
	TierSelfHosted   = "self_hosted"

	DeploymentCloud      = "cloud"
	DeploymentSelfHosted = "self_hosted"

	FeatureConsole = "console" // browser SSH/RDP/VNC
	FeatureOIDC    = "oidc"    // per-instance single sign-on
)

// Unlimited is the sentinel for "no cap" in every Limits field.
const Unlimited = -1

// Limits are the countable caps a licence grants.
type Limits struct {
	MaxServers      int `json:"max_servers"`
	MaxSecretGroups int `json:"max_secret_groups"`
	MaxChannels     int `json:"max_channels"`
}

// License is the signed payload.
//
// InstanceID is always populated: the self-hosted purchase flow links the
// instance UUID before the licence is signed, so there is no unbound licence
// and no claim protocol.
type License struct {
	ID           string    `json:"id"`            // uuid, for support and audit
	InstanceID   string    `json:"instance_id"`   // the instance this licence is bound to
	AccountID    string    `json:"account_id"`    // admin-side customer, informational
	InstanceName string    `json:"instance_name"` // display only
	Tier         string    `json:"tier"`
	Deployment   string    `json:"deployment"`
	IssuedAt     time.Time `json:"issued_at"`
	ExpiresAt    time.Time `json:"expires_at"`
	Limits       Limits    `json:"limits"`
	Features     []string  `json:"features"`
}

// HasFeature reports whether the licence grants a named feature.
//
// Callers must use this rather than switching on Tier. Adding a tier, or
// changing what a tier includes, must never require a server release.
func (l License) HasFeature(name string) bool {
	for _, f := range l.Features {
		if f == name {
			return true
		}
	}
	return false
}

// WithinLimit reports whether one more of something is allowed.
// A max of Unlimited always allows.
func WithinLimit(current, max int) bool {
	if max == Unlimited {
		return true
	}
	return current < max
}
