import { Client, Doc, DocumentQuery, Domain, FindOptions, IncOptions, ObjQueryType, PushOptions } from '@anticrm/core'

/**
 * @public
 */
export type MigrateUpdate<T extends Doc> = Partial<T> & Omit<PushOptions<T>, '$move'> & IncOptions<T> & {
  // For any other mongo stuff
  [key: string]: any
}

/**
 * @public
 */
export interface MigrationResult {
  matched: number
  updated: number
}

/**
 * @public
 */
export type MigrationDocumentQuery<T extends Doc> = {
  [P in keyof T]?: ObjQueryType<T[P]> | null
} & {
  $search?: string
  // support nested queries e.g. 'user.friends.name'
  // this will mark all unrecognized properties as any (including nested queries)
  [key: string]: any
}

/**
 * @public
 * Client to perform model upgrades
 */
export interface MigrationClient {
  // Raw collection operations

  // Raw FIND, allow to find documents inside domain.
  find: <T extends Doc>(domain: Domain, query: MigrationDocumentQuery<T>, options?: Omit<FindOptions<T>, 'lookup'>) => Promise<T[]>

  // Allow to raw update documents inside domain.
  update: <T extends Doc>(domain: Domain, query: MigrationDocumentQuery<T>, operations: MigrateUpdate<T>) => Promise<MigrationResult>

  // Move documents per domain
  move: <T extends Doc>(sourceDomain: Domain, query: DocumentQuery<T>, targetDomain: Domain) => Promise<MigrationResult>
}

/**
 * @public
 */
export type MigrationUpgradeClient = Client

/**
 * @public
 */
export interface MigrateOperation {
  // Perform low level migration
  migrate: (client: MigrationClient) => Promise<void>
  // Perform high level upgrade operations.
  upgrade: (client: MigrationUpgradeClient) => Promise<void>
}
