// @oagen-ignore-file

package workos

import (
	
	
	
	
	
	
	
	
)

// SessionData represents the unsealed session cookie data.
type SessionData struct {
	AccessToken  string                            `json:"access_token"`
	RefreshToken string                            `json:"refresh_token"`
	User         *User                             `json:"user,omitempty"`
	Impersonator *AuthenticateResponseImpersonator `json:"impersonator,omitempty"`
}

// AuthenticateSessionResult holds the result of authenticating a session.
type AuthenticateSessionResult struct {
	Authenticated  bool
	SessionID      string
	OrganizationID string
	Role           string
	Permissions    []string
	Entitlements   []string
	User           *User
	Impersonator   *AuthenticateResponseImpersonator
	// NeedsRefresh is true when the session cookie was structurally valid
	// but the access-token JWT has expired. Callers should refresh the
	// session (e.g. via Session.Refresh) before treating the user as
	// unauthenticated.
	NeedsRefresh bool
	Reason       string // populated on failure: "no_session_cookie_provided", "invalid_session_cookie", "invalid_jwt", "session_expired", etc.
}

// JWTClaims represents the claims extracted from a session JWT payload.
type JWTClaims struct {
	SessionID      string   `json:"sid"`
	OrganizationID string   `json:"org_id"`
	Role           string   `json:"role"`
	Permissions    []string `json:"permissions"`
	Entitlements   []string `json:"entitlements"`
	// Exp is the JWT expiration claim (seconds since the Unix epoch). Zero
	// when the token did not include an `exp` claim.
	Exp int64 `json:"exp"`
}

// RefreshSessionResult holds the result of refreshing a session.
type RefreshSessionResult struct {
	Authenticated bool
	SealedSession string
	Session       *SessionData
	Reason        string
}

// Session provides session cookie management.
type Session struct {
	client         *Client
	cookiePassword string
	sessionData    string // sealed session cookie value
}

// NewSession creates a new Session helper.
func ( *Client,  string,  string) *Session {
	return &Session{
		client:         ,
		cookiePassword: ,
		sessionData:    ,
	}
}

// Authenticate validates the session cookie.
// Unseals the session data, validates that the access token is present,
// and extracts claims from the JWT payload.
func ( *Session) () (*AuthenticateSessionResult, error) {
	if .sessionData == "" {
		return &AuthenticateSessionResult{
			Authenticated: false,
			Reason:        "no_session_cookie_provided",
		}, nil
	}

	,  := unsealSession(.sessionData, .cookiePassword)
	if  != nil {
		return &AuthenticateSessionResult{
			Authenticated: false,
			Reason:        "invalid_session_cookie",
		}, nil
	}

	if .AccessToken == "" {
		return &AuthenticateSessionResult{
			Authenticated: false,
			Reason:        "invalid_jwt",
		}, nil
	}

	,  := parseJWTPayload(.AccessToken)
	if  != nil {
		return &AuthenticateSessionResult{
			Authenticated: false,
			Reason:        "invalid_jwt",
		}, nil
	}

	// Enforce JWT expiration. Treat tokens whose `exp` claim is in the past
	// as expired and signal that the caller should refresh the session.
	if .Exp != 0 && time.Now().Unix() >= .Exp {
		return &AuthenticateSessionResult{
			Authenticated:  false,
			NeedsRefresh:   true,
			SessionID:      .SessionID,
			OrganizationID: .OrganizationID,
			Role:           .Role,
			Permissions:    .Permissions,
			Entitlements:   .Entitlements,
			User:           .User,
			Impersonator:   .Impersonator,
			Reason:         "session_expired",
		}, nil
	}

	return &AuthenticateSessionResult{
		Authenticated:  true,
		SessionID:      .SessionID,
		OrganizationID: .OrganizationID,
		Role:           .Role,
		Permissions:    .Permissions,
		Entitlements:   .Entitlements,
		User:           .User,
		Impersonator:   .Impersonator,
	}, nil
}

// Refresh refreshes the session using the refresh token.
//
// On authentication-level failures (revoked refresh token, transient upstream
// errors) the returned error is non-nil and the result carries
// Authenticated=false with a Reason of "refresh_token_revoked" or
// "refresh_failed". Callers should check result.Authenticated (not err == nil)
// as the success signal.
func ( *Session) ( context.Context,  ...RequestOption) (*RefreshSessionResult, error) {
	if .sessionData == "" {
		return &RefreshSessionResult{
			Authenticated: false,
			Reason:        "no_session_cookie_provided",
		}, nil
	}

	,  := unsealSession(.sessionData, .cookiePassword)
	if  != nil {
		return &RefreshSessionResult{
			Authenticated: false,
			Reason:        "invalid_session_cookie",
		}, nil
	}

	if .RefreshToken == "" {
		return &RefreshSessionResult{
			Authenticated: false,
			Reason:        "no_refresh_token",
		}, nil
	}

	if .client == nil {
		return nil, errors.New("workos: client is required for session refresh")
	}

	// Extract organization_id from the JWT claims for the refresh request.
	var  *string
	if .AccessToken != "" {
		if ,  := parseJWTPayload(.AccessToken);  == nil && .OrganizationID != "" {
			 = &.OrganizationID
		}
	}

	,  := .client.UserManagement().AuthenticateWithRefreshToken(, &UserManagementAuthenticateWithRefreshTokenParams{
		RefreshToken:   .RefreshToken,
		OrganizationID: ,
	}, ...)
	if  != nil {
		 := "refresh_failed"
		var  *APIError
		if errors.As(, &) && .StatusCode == 401 && .ErrorCode == "invalid_grant" {
			 = "refresh_token_revoked"
		}
		return &RefreshSessionResult{
			Authenticated: false,
			Reason:        ,
		}, 
	}

	 := &SessionData{
		AccessToken:  .AccessToken,
		RefreshToken: .RefreshToken,
		User:         .User,
		Impersonator: .Impersonator,
	}

	,  := SealSession(, .cookiePassword)
	if  != nil {
		return nil, fmt.Errorf("workos: failed to seal refreshed session: %w", )
	}

	return &RefreshSessionResult{
		Authenticated: true,
		SealedSession: ,
		Session:       ,
	}, nil
}

// GetLogoutURL returns a logout URL for the session.
// The returnTo parameter is optional — pass an empty string to omit it.
func ( *Session) ( context.Context,  string,  ...RequestOption) (string, error) {
	if .sessionData == "" {
		return "", errors.New("workos: no session data provided")
	}

	// Extract the session ID from the cookie. We deliberately do not require
	// result.Authenticated here: an expired access token (Authenticated=false,
	// Reason=session_expired) still has a valid SessionID, and logging out
	// after expiry is the common case. The WorkOS logout endpoint accepts the
	// session ID regardless of access-token freshness.
	,  := .Authenticate()
	if  != nil {
		return "", fmt.Errorf("workos: failed to authenticate session: %w", )
	}
	if .SessionID == "" {
		return "", errors.New("workos: session has no session ID")
	}

	 := defaultBaseURL
	if .client != nil && .client.baseURL != "" {
		 = .client.baseURL
	}

	 := fmt.Sprintf("%s/user_management/sessions/logout?session_id=%s", , url.QueryEscape(.SessionID))
	if  != "" {
		 += "&return_to=" + url.QueryEscape()
	}

	return , nil
}

// SealSessionFromAuthResponse creates a sealed session cookie from an authentication response.
func ( string,  string,  *User,  *AuthenticateResponseImpersonator,  string) (string, error) {
	 := &SessionData{
		AccessToken:  ,
		RefreshToken: ,
		User:         ,
		Impersonator: ,
	}
	return SealSession(, )
}

// AuthenticateSession is a convenience method for one-shot session authentication.
// It does not require a Client — only the sealed session and cookie password.
func ( string,  string) (*AuthenticateSessionResult, error) {
	 := NewSession(nil, , )
	return .Authenticate()
}

// RefreshSession is a convenience method on Client for one-shot session refresh.
func ( *Client) ( context.Context,  string,  string,  ...RequestOption) (*RefreshSessionResult, error) {
	 := NewSession(, , )
	return .Refresh(, ...)
}

// parseJWTPayload extracts and decodes the payload (claims) from a JWT.
// It does not verify the signature — this is acceptable because the JWT was
// sealed by us and is trusted after unsealing.
func ( string) (*JWTClaims, error) {
	 := strings.Split(, ".")
	if len() != 3 {
		return nil, fmt.Errorf("workos: invalid JWT format: expected 3 parts, got %d", len())
	}

	 := [1]

	// Base64url decode — the standard library's RawURLEncoding handles the
	// URL-safe alphabet and no-padding variant used by JWTs.
	,  := base64.RawURLEncoding.DecodeString()
	if  != nil {
		return nil, fmt.Errorf("workos: failed to decode JWT payload: %w", )
	}

	var  JWTClaims
	if  := json.Unmarshal(, &);  != nil {
		return nil, fmt.Errorf("workos: failed to unmarshal JWT claims: %w", )
	}

	return &, nil
}