package jwtauth

import "context"

type Claims struct {
	UserID string
	Groups []string
	// JTI and Scopes are populated only for HS256 shortcut tokens; a full OIDC /
	// dev session leaves them empty. A non-empty JTI marks a *scoped* token —
	// one allowed to reach only the endpoints its Scopes grant (the route layer
	// enforces this). This keeps a leaked shortcut token's blast radius minimal.
	JTI    string
	Scopes []string
	// SessionScope is set only for cdrop self-signed session tokens (scan-login):
	// "full" or "guest". Empty for OIDC / dev sessions and scoped shortcut tokens.
	// A "guest" session is capability-limited (requireFullSession rejects it on
	// account-management routes) even though it is not Scoped().
	SessionScope string
}

// Scoped reports whether these claims came from a scoped shortcut token rather
// than a full login session.
func (c *Claims) Scoped() bool { return c.JTI != "" }

// Guest reports whether these claims came from a restricted guest session (a
// scan-login borrow). Guest sessions can transfer files but not manage the
// account, approve other devices, or mint long-lived tokens.
func (c *Claims) Guest() bool { return c.SessionScope == "guest" }

// HasScope reports whether the claims grant the named scope.
func (c *Claims) HasScope(scope string) bool {
	for _, s := range c.Scopes {
		if s == scope {
			return true
		}
	}
	return false
}

type ctxKey int

const (
	claimsCtxKey ctxKey = iota
	deviceCtxKey
	deviceTypeCtxKey
)

func ClaimsFromContext(ctx context.Context) (*Claims, bool) {
	c, ok := ctx.Value(claimsCtxKey).(*Claims)
	return c, ok
}

// ContextWithClaims attaches claims to a context — the inverse of
// ClaimsFromContext. The auth middleware uses this; it is also the seam handlers
// and tests use to inject claims directly.
func ContextWithClaims(ctx context.Context, c *Claims) context.Context {
	return context.WithValue(ctx, claimsCtxKey, c)
}

func DeviceNameFromContext(ctx context.Context) (string, bool) {
	n, ok := ctx.Value(deviceCtxKey).(string)
	return n, ok
}

// DeviceTypeFromContext returns the client-declared device type set by the auth
// middleware (browser / macos / windows / linux), defaulting to "browser".
func DeviceTypeFromContext(ctx context.Context) string {
	t, ok := ctx.Value(deviceTypeCtxKey).(string)
	if !ok || t == "" {
		return "browser"
	}
	return t
}
