package services

// Per-turn supabase JWT minting for Paliadin (t-paliad-156, folded into
// t-paliad-194 / m/paliad#38 Phase B).
//
// Each Paliadin turn carries a short-lived JWT scoped to the calling
// user. The JWT is signed with paliad's existing SUPABASE_JWT_SECRET so
// it has the same shape Supabase Auth itself issues — same claims, same
// signature, same role. The aichat backend writes it to a per-turn file
// the claude pane reads to `SET LOCAL request.jwt.claims = …` before
// every paliad.* query, which makes RLS evaluate as the user.
//
// TTL: short (default 2 min) — long enough to cover the persona's 120 s
// run-turn budget plus generous slack for queueing, short enough that a
// leaked JWT is uninteresting. Each turn mints fresh; nothing is cached.

import (
	"errors"
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
)

// ErrJWTSecretMissing signals that mintTurnJWT was called without the
// SUPABASE_JWT_SECRET configured. paliad's auth layer fails fast on the
// same condition at boot, but the per-turn mint path is reachable from
// tests + the disabled stub, so we surface a typed error rather than
// panicking.
var ErrJWTSecretMissing = errors.New("paliadin: SUPABASE_JWT_SECRET not configured")

// DefaultPaliadinJWTTTL is the JWT lifetime when the caller doesn't
// override. 2 minutes covers aichat's 120 s persona timeout plus a few
// seconds of buffer for HTTP overhead and clock skew.
const DefaultPaliadinJWTTTL = 2 * time.Minute

// mintTurnJWT signs a Supabase-shaped access token for the given user.
// Claims:
//
//	sub  : userID — RLS reads this via auth.uid()
//	role : "authenticated" — required so SET LOCAL ROLE matches
//	aud  : "authenticated" — Supabase convention
//	iss  : "paliad/paliadin" — distinguishes from real GoTrue tokens in
//	       audit traces; not validated by RLS
//	iat  : now
//	exp  : now + ttl
//
// Signed HS256 with SUPABASE_JWT_SECRET (same secret paliad already
// verifies session cookies against in internal/auth.Client). The
// returned string is a standard 3-segment JWT.
func mintTurnJWT(userID uuid.UUID, ttl time.Duration, secret []byte) (string, error) {
	if len(secret) == 0 {
		return "", ErrJWTSecretMissing
	}
	if ttl <= 0 {
		ttl = DefaultPaliadinJWTTTL
	}
	now := time.Now()
	claims := jwt.MapClaims{
		"sub":  userID.String(),
		"role": "authenticated",
		"aud":  "authenticated",
		"iss":  "paliad/paliadin",
		"iat":  now.Unix(),
		"exp":  now.Add(ttl).Unix(),
	}
	tok := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signed, err := tok.SignedString(secret)
	if err != nil {
		return "", fmt.Errorf("paliadin: sign turn JWT: %w", err)
	}
	return signed, nil
}
