Source: utils/authWorkspace.js

/**
 * @module utils/authWorkspace
 * @description Shared workspace-aware auth payload/response builders (ACL-001).
 */

import crypto from "crypto";
import * as workspaceRepo from "../database/repositories/workspaceRepo.js";

/**
 * Resolve the current workspace from a user's memberships.
 * Returns the workspace matching `hint`, or the first workspace as fallback.
 *
 * @param {Object[]} workspaces - Result of `workspaceRepo.getByUserId()`.
 * @param {string}   [hint]     - Preferred workspace ID.
 * @returns {Object|undefined}    The resolved workspace, or undefined if empty.
 */
function resolveCurrentWorkspace(workspaces, hint) {
  if (!workspaces || workspaces.length === 0) return undefined;
  return workspaces.find((ws) => ws.id === hint) || workspaces[0];
}

/**
 * @typedef {Object} JwtPayload
 * @property {string}            sub
 * @property {string}            email
 * @property {string}            name
 * @property {string}            role
 * @property {string}            jti
 * @property {string}            [workspaceId] - Present when the user has workspaces.
 */

/**
 * Build a JWT payload with a workspace hint.
 *
 * The JWT carries `workspaceId` as a routing hint only. Authorization is always
 * resolved from `workspace_members` at request time.
 *
 * @param {Object} user - User row from the database.
 * @param {string} [workspaceIdHint] - Preferred current workspace ID.
 * @returns {JwtPayload}
 */
export function buildJwtPayload(user, workspaceIdHint) {
  const jti = crypto.randomUUID();
  const payload = { sub: user.id, email: user.email, name: user.name, role: user.role, jti };

  const workspaces = workspaceRepo.getByUserId(user.id);
  const current = resolveCurrentWorkspace(workspaces, workspaceIdHint);
  if (current) {
    payload.workspaceId = current.id;
  }

  return payload;
}

/**
 * @typedef {Object} UserResponse
 * @property {string}      id
 * @property {string}      name
 * @property {string}      email
 * @property {string}      role
 * @property {string|null} avatar
 * @property {string}      [workspaceId]    - Active workspace ID (present when user has workspaces).
 * @property {string}      [workspaceName]  - Active workspace display name.
 * @property {string}      [workspaceRole]  - User's role in the active workspace.
 * @property {Array<{id: string, name: string, role: string, isOwner: boolean}>} [workspaces] - All workspaces (present when user belongs to 2+).
 */

/**
 * Build the user response object with workspace context for the frontend.
 *
 * @param {Object} user - User row from the database.
 * @param {string} [workspaceIdHint] - Preferred current workspace ID.
 * @returns {UserResponse}
 */
export function buildUserResponse(user, workspaceIdHint) {
  const resp = {
    id: user.id,
    name: user.name,
    email: user.email,
    role: user.role,
    avatar: user.avatar || null,
    hasPassword: !!user.passwordHash,
  };

  const workspaces = workspaceRepo.getByUserId(user.id);
  const current = resolveCurrentWorkspace(workspaces, workspaceIdHint);
  if (current) {
    resp.workspaceId = current.id;
    resp.workspaceName = current.name;
    resp.workspaceRole = current.role;
  }

  if (workspaces && workspaces.length > 1) {
    resp.workspaces = workspaces.map((ws) => ({
      id: ws.id,
      name: ws.name,
      role: ws.role,
      isOwner: ws.ownerId === user.id,
    }));
  }

  return resp;
}