// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import { z } from 'zod';

/**
 * Identity & User Model Specification
 * 
 * Defines the standard user, account, and session data models for ObjectStack.
 * These schemas represent "who is logged in" and their associated data.
 * 
 * This is separate from authentication configuration (auth.zod.ts) which
 * defines "how to login".
 */

/**
 * User Schema
 * Core user identity data model
 */
import { lazySchema } from '../shared/lazy-schema';
export const UserSchema = lazySchema(() => z.object({
  /**
   * Unique user identifier
   */
  id: z.string().describe('Unique user identifier'),
  
  /**
   * User's email address (primary identifier)
   */
  email: z.string().email().describe('User email address'),
  
  /**
   * Email verification status
   */
  emailVerified: z.boolean().default(false).describe('Whether email is verified'),
  
  /**
   * User's display name
   */
  name: z.string().optional().describe('User display name'),
  
  /**
   * User's profile image URL
   */
  image: z.string().url().optional().describe('Profile image URL'),
  
  /**
   * Account creation timestamp
   */
  createdAt: z.string().datetime().describe('Account creation timestamp'),
  
  /**
   * Last update timestamp
   */
  updatedAt: z.string().datetime().describe('Last update timestamp'),
}));

export type User = z.infer<typeof UserSchema>;

/**
 * Account Schema
 * Links external OAuth/OIDC/SAML accounts to a user
 */
export const AccountSchema = lazySchema(() => z.object({
  /**
   * Unique account identifier
   */
  id: z.string().describe('Unique account identifier'),
  
  /**
   * Associated user ID
   */
  userId: z.string().describe('Associated user ID'),
  
  /**
   * Account type/provider
   */
  type: z.enum([
    'oauth',
    'oidc',
    'email',
    'credentials',
    'saml',
    'ldap',
  ]).describe('Account type'),
  
  /**
   * Provider name (e.g., 'google', 'github', 'okta')
   */
  provider: z.string().describe('Provider name'),
  
  /**
   * Provider account ID
   */
  providerAccountId: z.string().describe('Provider account ID'),
  
  /**
   * OAuth refresh token
   */
  refreshToken: z.string().optional().describe('OAuth refresh token'),
  
  /**
   * OAuth access token
   */
  accessToken: z.string().optional().describe('OAuth access token'),
  
  /**
   * Token expiry timestamp
   */
  expiresAt: z.number().optional().describe('Token expiry timestamp (Unix)'),
  
  /**
   * OAuth token type
   */
  tokenType: z.string().optional().describe('OAuth token type'),
  
  /**
   * OAuth scope
   */
  scope: z.string().optional().describe('OAuth scope'),
  
  /**
   * OAuth ID token
   */
  idToken: z.string().optional().describe('OAuth ID token'),
  
  /**
   * Session state
   */
  sessionState: z.string().optional().describe('Session state'),
  
  /**
   * Account creation timestamp
   */
  createdAt: z.string().datetime().describe('Account creation timestamp'),
  
  /**
   * Last update timestamp
   */
  updatedAt: z.string().datetime().describe('Last update timestamp'),
}));

export type Account = z.infer<typeof AccountSchema>;

/**
 * Session Schema
 * User session data model
 */
export const SessionSchema = lazySchema(() => z.object({
  /**
   * Unique session identifier
   */
  id: z.string().describe('Unique session identifier'),
  
  /**
   * Session token
   */
  sessionToken: z.string().describe('Session token'),
  
  /**
   * Associated user ID
   */
  userId: z.string().describe('Associated user ID'),
  
  /**
   * Active organization ID for this session
   * Used for context switching in multi-tenant applications
   */
  activeOrganizationId: z.string().optional().describe('Active organization ID for context switching'),
  
  /**
   * Session expiry timestamp
   */
  expires: z.string().datetime().describe('Session expiry timestamp'),
  
  /**
   * Session creation timestamp
   */
  createdAt: z.string().datetime().describe('Session creation timestamp'),
  
  /**
   * Last update timestamp
   */
  updatedAt: z.string().datetime().describe('Last update timestamp'),
  
  /**
   * IP address of the session
   */
  ipAddress: z.string().optional().describe('IP address'),
  
  /**
   * User agent string
   */
  userAgent: z.string().optional().describe('User agent string'),
  
  /**
   * Device fingerprint
   */
  fingerprint: z.string().optional().describe('Device fingerprint'),
}));

export type Session = z.infer<typeof SessionSchema>;

/**
 * Verification Token Schema
 * Email verification and password reset tokens
 */
export const VerificationTokenSchema = lazySchema(() => z.object({
  /**
   * Token identifier (email or phone)
   */
  identifier: z.string().describe('Token identifier (email or phone)'),
  
  /**
   * Verification token
   */
  token: z.string().describe('Verification token'),
  
  /**
   * Token expiry timestamp
   */
  expires: z.string().datetime().describe('Token expiry timestamp'),
  
  /**
   * Token creation timestamp
   */
  createdAt: z.string().datetime().describe('Token creation timestamp'),
}));

export type VerificationToken = z.infer<typeof VerificationTokenSchema>;

/**
 * API Key Schema
 *
 * Aligns with better-auth's API key plugin capabilities.
 * Provides programmatic access to ObjectStack APIs (CI/CD, service-to-service, CLI).
 *
 * @see https://www.better-auth.com/docs/plugins/api-key
 */
export const ApiKeySchema = lazySchema(() => z.object({
  /**
   * Unique API key identifier
   */
  id: z.string().describe('API key identifier'),

  /**
   * Human-readable name for the key
   */
  name: z.string().describe('API key display name'),

  /**
   * Key prefix (visible portion for identification, e.g., "os_pk_ab")
   */
  start: z.string().optional().describe('Key prefix for identification'),

  /**
   * Custom prefix for the key (e.g., "os_pk_")
   */
  prefix: z.string().optional().describe('Custom key prefix'),

  /**
   * User ID of the key owner
   */
  userId: z.string().describe('Owner user ID'),

  /**
   * Organization ID the key is scoped to (optional)
   */
  organizationId: z.string().optional().describe('Scoped organization ID'),

  /**
   * Key expiration timestamp (null = never expires)
   */
  expiresAt: z.string().datetime().optional().describe('Expiration timestamp'),

  /**
   * Creation timestamp
   */
  createdAt: z.string().datetime().describe('Creation timestamp'),

  /**
   * Last update timestamp
   */
  updatedAt: z.string().datetime().describe('Last update timestamp'),

  /**
   * Last used timestamp
   */
  lastUsedAt: z.string().datetime().optional().describe('Last used timestamp'),

  /**
   * Last refetch timestamp (for cached permission checks)
   */
  lastRefetchAt: z.string().datetime().optional().describe('Last refetch timestamp'),

  /**
   * Whether this key is enabled
   */
  enabled: z.boolean().default(true).describe('Whether the key is active'),

  /**
   * Rate limiting: enabled flag
   */
  rateLimitEnabled: z.boolean().optional().describe('Whether rate limiting is enabled'),

  /**
   * Rate limiting: time window in milliseconds
   */
  rateLimitTimeWindow: z.number().int().min(0).optional().describe('Rate limit window (ms)'),

  /**
   * Rate limiting: max requests per window
   */
  rateLimitMax: z.number().int().min(0).optional().describe('Max requests per window'),

  /**
   * Rate limiting: remaining requests in current window
   */
  remaining: z.number().int().min(0).optional().describe('Remaining requests'),

  /**
   * Permissions assigned to this key (granular access control)
   */
  permissions: z.record(z.string(), z.boolean()).optional()
    .describe('Granular permission flags'),

  /**
   * Scopes assigned to this key (high-level access categories)
   */
  scopes: z.array(z.string()).optional()
    .describe('High-level access scopes'),

  /**
   * Custom metadata
   */
  metadata: z.record(z.string(), z.unknown()).optional().describe('Custom metadata'),
}));

export type ApiKey = z.infer<typeof ApiKeySchema>;
