# Core domain types: Tenant, User, TenantSettings, TenantMember, UserProfile

type Tenant {
  id: ID!
  name: String!
  slug: String!
  plan: String!
  issuePrefix: String
  issueCounter: Int!
  """
  Sandbox kill switch. When false, the dispatcher does not register the
  execute_code tool regardless of template opt-in. Default-true for new
  tenants; the migration that added this column flipped every pre-existing
  tenant to false so Phase 3b enforcement lands before the sandbox runs.
  """
  sandboxEnabled: Boolean!
  """
  Compliance classification: "standard" | "regulated" | "hipaa". Only
  standard tenants may enable the sandbox; a compound CHECK on the tenants
  table enforces this at the schema layer.
  """
  complianceTier: String!
  """
  Per-tenant AgentCore Code Interpreter IDs, populated asynchronously by
  the agentcore-admin Lambda (plan Unit 5). Null during the provisioning
  window.
  """
  sandboxInterpreterPublicId: String
  sandboxInterpreterInternalId: String
  """
  Per-tenant kill switches for built-in tools (plan #007 R6/R7). Array of
  slug strings (e.g. ["execute_code", "web_search"]). Empty array = all
  built-ins available (subject to template blocks). The runtime applies
  this as a narrow-only filter at Agent(tools=...) construction; template
  blocks intersect (a template cannot unblock what the tenant disabled).
  Admin UI for editing this field defers to a follow-up PR; until then
  operators mutate the column directly.
  """
  disabledBuiltinTools: [String!]!
  settings: TenantSettings
  members: [TenantMember!]!
  agents: [Agent!]!
  teams: [Team!]!
  createdAt: AWSDateTime!
  updatedAt: AWSDateTime!
}

type TenantSettings {
  id: ID!
  tenantId: ID!
  defaultModel: String
  budgetMonthlyCents: Int
  autoCloseThreadMinutes: Int
  maxAgents: Int
  features: AWSJSON
  createdAt: AWSDateTime!
  updatedAt: AWSDateTime!
}

type TenantMember {
  id: ID!
  tenantId: ID!
  principalType: String!
  principalId: ID!
  role: String!
  status: String!
  user: User
  agent: Agent
  createdAt: AWSDateTime!
  updatedAt: AWSDateTime!
}

type User {
  id: ID!
  tenantId: ID!
  email: String!
  name: String
  image: String
  phone: String
  profile: UserProfile
  createdAt: AWSDateTime!
  updatedAt: AWSDateTime!
}

type UserProfile {
  id: ID!
  userId: ID!
  tenantId: ID!
  displayName: String
  theme: String
  notificationPreferences: AWSJSON
  operatingModel: AWSJSON
  operatingModelHistory: [AWSJSON!]!
  title: String
  timezone: String
  pronouns: String
  "Short/preferred name — what the agent should call this human in chat."
  callBy: String
  "Free-form notes the agent maintains about this human's preferences + style."
  notes: String
  "Free-form markdown describing the human's family / close contacts."
  family: String
  "Free-form markdown capturing ongoing context about the human."
  context: String
  createdAt: AWSDateTime!
  updatedAt: AWSDateTime!
}

# --- Deployment Status (read from Lambda env vars) ---

type DeploymentStatus {
  stage: String!
  source: String!
  region: String!
  accountId: String
  bucketName: String
  databaseEndpoint: String
  ecrUrl: String
  adminUrl: String
  docsUrl: String
  apiEndpoint: String
  appsyncUrl: String
  appsyncRealtimeUrl: String
  hindsightEndpoint: String
  agentcoreStatus: String
  hindsightEnabled: Boolean!
  managedMemoryEnabled: Boolean!
}

# --- Inputs ---

input CreateTenantInput {
  name: String!
  slug: String!
  plan: String
}

input UpdateTenantInput {
  name: String
  plan: String
  issuePrefix: String
  """
  Optional client-supplied idempotency key. When provided, a retry with
  the same key returns the prior call's result without re-executing.
  Null/absent = server derives a key from canonicalized inputs.
  See packages/api/src/lib/idempotency.ts.
  """
  idempotencyKey: String
}

input UpdateTenantSettingsInput {
  defaultModel: String
  budgetMonthlyCents: Int
  autoCloseThreadMinutes: Int
  maxAgents: Int
  features: AWSJSON
}

"""
Platform-operator-only input for sandbox + compliance-tier policy changes
(plan Unit 6). Separate from UpdateTenantInput because these fields shift
the tenant's security boundary and are audited in tenant_policy_events.
Caller must be in the THINKWORK_PLATFORM_OPERATOR_EMAILS allowlist on the
graphql-http Lambda.
"""
input UpdateTenantPolicyInput {
  "Sandbox kill switch. Setting true while complianceTier != 'standard' is rejected."
  sandboxEnabled: Boolean
  "Compliance tier: 'standard' | 'regulated' | 'hipaa'. Non-standard coerces sandboxEnabled = false."
  complianceTier: String
}

input AddTenantMemberInput {
  principalType: String!
  principalId: ID!
  role: String
  "Optional idempotency key. See UpdateTenantInput.idempotencyKey."
  idempotencyKey: String
}

input UpdateTenantMemberInput {
  role: String
  status: String
  "Optional idempotency key. See UpdateTenantInput.idempotencyKey."
  idempotencyKey: String
}

input UpdateUserInput {
  name: String
  image: String
  phone: String
}

input UpdateUserProfileInput {
  displayName: String
  theme: String
  notificationPreferences: AWSJSON
  operatingModel: AWSJSON
  operatingModelHistory: [AWSJSON!]
  title: String
  timezone: String
  pronouns: String
  "Short/preferred name the agent should use in chat. Set via admin UI or agent self-serve tool."
  callBy: String
  "Free-form notes about the human's preferences + communication style."
  notes: String
  "Free-form markdown describing the human's family / close contacts."
  family: String
  "Free-form markdown capturing ongoing context about the human."
  context: String
}

# --- Query / Mutation extensions ---

extend type Query {
  tenant(id: ID!): Tenant
  tenantBySlug(slug: String!): Tenant
  me: User
  user(id: ID!): User
  tenantMembers(tenantId: ID!): [TenantMember!]!
  deploymentStatus: DeploymentStatus!
  """
  Returns the caller's own role on the caller's own tenant.

  Used by the thinkwork-admin Python skill to pre-flight the
  server-side role gate before making a gated mutation. The query
  takes no arguments by design — it cannot be used as an enumeration
  oracle to probe arbitrary (userId, tenantId) pairs. The
  authoritative role gate remains `requireAdminOrApiKeyCaller` on
  each gated mutation.
  """
  adminRoleCheck: AdminRoleCheckResult!
}

type AdminRoleCheckResult {
  """
  One of: owner, admin, member, other.
  """
  role: String!
}

input InviteMemberInput {
  email: String!
  name: String
  role: String
  "Optional idempotency key. See UpdateTenantInput.idempotencyKey."
  idempotencyKey: String
}

type BootstrapResult {
  user: User!
  tenant: Tenant!
  isNew: Boolean!
}

extend type Mutation {
  bootstrapUser: BootstrapResult!
  createTenant(input: CreateTenantInput!): Tenant!
  updateTenant(id: ID!, input: UpdateTenantInput!): Tenant!
  """
  Platform-operator-only mutation — see UpdateTenantPolicyInput. Changes
  are audited in tenant_policy_events and must satisfy the compound
  sandbox_requires_standard_tier CHECK on the tenants table.
  """
  updateTenantPolicy(tenantId: ID!, input: UpdateTenantPolicyInput!): Tenant!
  updateTenantSettings(
    tenantId: ID!
    input: UpdateTenantSettingsInput!
  ): TenantSettings!
  addTenantMember(tenantId: ID!, input: AddTenantMemberInput!): TenantMember!
  updateTenantMember(id: ID!, input: UpdateTenantMemberInput!): TenantMember!
  "Remove a tenant member. idempotencyKey optional — see UpdateTenantInput.idempotencyKey."
  removeTenantMember(id: ID!, idempotencyKey: String): Boolean!
  inviteMember(tenantId: ID!, input: InviteMemberInput!): TenantMember!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  updateUserProfile(userId: ID!, input: UpdateUserProfileInput!): UserProfile!
  registerPushToken(input: RegisterPushTokenInput!): Boolean!
  unregisterPushToken(token: String!): Boolean!
}

input RegisterPushTokenInput {
  token: String!
  platform: String!
}
