// src/server/entities/invitations.ts
import { text as text3, timestamp as timestamp2, bigint as bigint2, index as index3, jsonb } from "drizzle-orm/pg-core";
import { id as id3, timestamps as timestamps3 } from "@spfn/core/db";

// src/server/entities/roles.ts
import { text, boolean, integer, index } from "drizzle-orm/pg-core";
import { id, timestamps } from "@spfn/core/db";

// src/server/entities/schema.ts
import { createFunctionSchema } from "@spfn/core/db";
var authSchema = createFunctionSchema("@spfn/auth");

// src/server/entities/roles.ts
var roles = authSchema.table(
  "roles",
  {
    // Primary key
    id: id(),
    // Role identifier (used in code, e.g., 'admin', 'editor')
    // Must be unique, lowercase, kebab-case recommended
    name: text("name").notNull().unique(),
    // Display name for UI (e.g., 'Administrator', 'Content Editor')
    displayName: text("display_name").notNull(),
    // Role description
    description: text("description"),
    // Built-in role flag
    // true: Core package roles (user, admin, superadmin) - cannot be deleted
    // false: Custom or preset roles - can be deleted
    isBuiltin: boolean("is_builtin").notNull().default(false),
    // System role flag
    // true: Defined in code (builtin or preset) - deletion restricted
    // false: Runtime created custom role - fully manageable
    isSystem: boolean("is_system").notNull().default(false),
    // Active status
    // false: Deactivated role (users cannot be assigned)
    isActive: boolean("is_active").notNull().default(true),
    // Priority level (higher = more privileged)
    // superadmin: 100, admin: 80, user: 10
    // Used for role hierarchy and conflict resolution
    priority: integer("priority").notNull().default(10),
    ...timestamps()
  },
  (table) => [
    index("roles_name_idx").on(table.name),
    index("roles_is_system_idx").on(table.isSystem),
    index("roles_is_active_idx").on(table.isActive),
    index("roles_is_builtin_idx").on(table.isBuiltin),
    index("roles_priority_idx").on(table.priority)
  ]
);

// src/server/entities/users.ts
import { text as text2, timestamp, check, boolean as boolean2, bigint, index as index2 } from "drizzle-orm/pg-core";
import { id as id2, timestamps as timestamps2 } from "@spfn/core/db";
import { sql } from "drizzle-orm";
var users = authSchema.table(
  "users",
  {
    // Identity
    id: id2(),
    // Email address (unique identifier)
    // Used for: login, password reset, notifications
    email: text2("email").unique(),
    // Phone number in E.164 international format
    // Format: +[country code][number] (e.g., +821012345678)
    // Used for: SMS login, 2FA, notifications
    phone: text2("phone").unique(),
    // Authentication
    // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)
    // Nullable to support OAuth-only accounts
    passwordHash: text2("password_hash"),
    // Force password change on next login
    // Use cases: initial setup, security breach, policy violation
    passwordChangeRequired: boolean2("password_change_required").notNull().default(false),
    // Authorization (Role-Based Access Control)
    // Foreign key to roles table
    // References built-in roles: user (default), admin, superadmin
    // Can also reference custom roles created at runtime
    roleId: bigint("role_id", { mode: "number" }).references(() => roles.id).notNull(),
    // Account status
    // - active: Normal operation (default)
    // - inactive: Deactivated (user request, dormant)
    // - suspended: Locked (security incident, ToS violation)
    status: text2(
      "status",
      {
        enum: ["active", "inactive", "suspended"]
      }
    ).notNull().default("active"),
    // Verification timestamps
    // null = unverified, timestamp = verified at this time
    // Email verification (via verification code or magic link)
    emailVerifiedAt: timestamp("email_verified_at", { withTimezone: true }),
    // Phone verification (via SMS OTP)
    phoneVerifiedAt: timestamp("phone_verified_at", { withTimezone: true }),
    // Metadata
    // Last successful login timestamp
    // Used for: security auditing, dormant account detection
    lastLoginAt: timestamp("last_login_at", { withTimezone: true }),
    ...timestamps2()
  },
  (table) => [
    // Database constraints
    // Ensure at least one identifier exists (email OR phone)
    check(
      "email_or_phone_check",
      sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`
    ),
    // Indexes for query optimization
    index2("users_email_idx").on(table.email),
    index2("users_phone_idx").on(table.phone),
    index2("users_status_idx").on(table.status),
    index2("users_role_id_idx").on(table.roleId)
  ]
);

// src/server/entities/invitations.ts
var invitations = authSchema.table(
  "user_invitations",
  {
    // Primary key
    id: id3(),
    // Target email address for the invitation
    // Will become the user's email upon acceptance
    email: text3("email").notNull(),
    // Unique invitation token (UUID v4)
    // Used in invitation URL: /auth/invite/{token}
    // Single-use token that expires after acceptance
    token: text3("token").notNull().unique(),
    // Role to be assigned when invitation is accepted
    // Foreign key to roles table
    roleId: bigint2("role_id", { mode: "number" }).references(() => roles.id).notNull(),
    // User who created this invitation
    // Foreign key to users table
    // Used for: audit trail, permission checks
    invitedBy: bigint2("invited_by", { mode: "number" }).references(() => users.id).notNull(),
    // Invitation status
    // - pending: Invitation sent, awaiting acceptance
    // - accepted: User accepted and account created
    // - expired: Invitation expired (automatic)
    // - cancelled: Invitation cancelled by admin
    status: text3(
      "status",
      {
        enum: ["pending", "accepted", "expired", "cancelled"]
      }
    ).notNull().default("pending"),
    // Expiration timestamp (default: 7 days from creation)
    // Invitation cannot be accepted after this time
    // Background job should update status to 'expired'
    expiresAt: timestamp2("expires_at", { withTimezone: true }).notNull(),
    // Timestamp when invitation was accepted
    // null = not yet accepted
    // Used for: audit trail, analytics
    acceptedAt: timestamp2("accepted_at", { withTimezone: true }),
    // Timestamp when invitation was cancelled
    // null = not cancelled
    // Used for: audit trail
    cancelledAt: timestamp2("cancelled_at", { withTimezone: true }),
    // Additional metadata (JSONB)
    // Use cases:
    // - Custom welcome message
    // - Onboarding instructions
    // - Team/department assignment
    // - Custom fields for app-specific data
    // Example: { message: "Welcome!", department: "Engineering" }
    metadata: jsonb("metadata"),
    ...timestamps3()
  },
  (table) => [
    // Indexes for query optimization
    index3("invitations_token_idx").on(table.token),
    index3("invitations_email_idx").on(table.email),
    index3("invitations_status_idx").on(table.status),
    index3("invitations_invited_by_idx").on(table.invitedBy),
    index3("invitations_expires_at_idx").on(table.expiresAt),
    // For cleanup jobs
    index3("invitations_role_id_idx").on(table.roleId)
  ]
);
export {
  invitations
};
//# sourceMappingURL=invitations.js.map