/**
 * @import {DataModel, Document} from "./_module.mjs"
 * @import {DOCUMENT_OWNERSHIP_LEVELS, USER_ROLES} from "@common/constants.mjs"
 * @import {DataField} from "@common/data/fields.mjs";
 * @import BaseUser from "@common/documents/user.mjs"
 */

/**
 * @typedef {Record<string, DataField>} DataSchema
 */

/**
 * @typedef _DocumentConstructionContext
 * @property {Document|null} [parent=null]    The parent Document of this one, if this one is embedded
 * @property {string|null} [pack=null]        The compendium collection ID which contains this Document, if any
 * @property {string|null} [parentCollection] The name of the parent Document's collection that would contain this one
 *
 * @property {boolean} [creation]             In a ServerDocument context, is this document being created?
 * @property {number} [modifiedTime]          In a ServerDocument context, the timestamp of creation
 * @property {User} [user]                    In a ServerDocument context, the User performing the construction
 */

/**
 * @typedef {DataModelConstructionContext & _DocumentConstructionContext} DocumentConstructionContext
 */

/**
 * @typedef DataModelValidationOptions
 * @property {boolean} [fields=true]      Validate each field. Ignored if changes are passed.
 * @property {boolean} [joint=true]       Perform joint validation on the full data model.
 *                                        Ignored if changes are passed.
 * @property {object} [changes]           A specific set of proposed changes to validate, rather than the full
 *                                        source data of the model. This type of validation is redirected to
 *                                        DataModel#updateSource as a dry-run.
 * @property {Omit<DataModelCleaningOptions, "partial">|boolean} [clean]  Configuration of data cleaning
 *                                        steps applied to user input data. This can be explicitly passed as `false`
 *                                        which requires the provided data to already be clean.
 * @property {boolean} [fallback=false]   Allow replacement of invalid values with valid defaults? This option mutates
 *                                        the provided changes.
 * @property {boolean} [dropInvalidEmbedded=false]  If true, invalid embedded documents will emit a warning and be
 *                                        placed in the invalidDocuments collection rather than causing the parent
 *                                        to be considered invalid. This option mutates the provided changes.
 * @property {boolean} [dropInvalidElements=false] For Array-like fields, control whether an invalid element is
 *                                        allowed to be removed from the data in lieu of fallback replacement.
 * @property {DataModel} [model]          The data model instance being validated. This is automatically populated
 *                                        during the model validation flow.
 * @property {boolean} [strict=true]      Throw an error if validation fails.
 */

/**
 * @typedef _DataModelConstructionOptions
 * @property {DataModel|null} [parent]    A parent DataModel instance to which this DataModel belongs
 * @property {DataModelSchemaField} [schema]  If we are constructing an embedded model, it may be provided by the
 *                                        constructing DataModelSchemaField with an instance-specific schema to use
 * @property {Omit<DataModelCleaningOptions, "partial">|boolean} [clean]  Configuration of data cleaning
 *                                        steps applied to user input data. This can be explicitly passed as `false`
 *                                        to skip preprocessing when using data that is known to already be fully
 *                                        cleaned.
 */

/**
 * @typedef {_DataModelConstructionOptions &
 *           Pick<DataModelValidationOptions, "dropInvalidEmbedded"|"fallback"|"strict">} DataModelConstructionContext
 */

/**
 * @typedef DataModelUpdateOptions
 * @property {Omit<DataModelCleaningOptions, "partial">|boolean} [clean]  Configuration of data cleaning
 *                                        steps applied to user input data. This can be explicitly passed as `false`
 *                                        to skip preprocessing when using data that is known to already be fully
 *                                        cleaned.
 * @property {boolean} [dryRun=false]     Do not finally apply the change, but instead simulate the update workflow
 * @property {boolean} [fallback=false]   Allow automatic fallback to a valid initial value if the value provided for
 *                                        a field in the model is invalid.
 * @property {boolean} [recursive=true]   Apply changes to inner objects recursively rather than replacing the
 *                                        top-level object. This is automatically re-interpreted as every key of the
 *                                        update being defined as a ForcedReplacement operator.
 * @property {boolean} [restoreDelta]     An advanced option used specifically and internally by the ActorDelta model
 * @property {User} [user]                Used only by the server to understand update context
 */


/**
 * @typedef DataModelUpdateState
 * @property {DataModel} model
 * @property {object} source
 * @property {object} diff
 * @property {DataModelValidationFailure} failure
 */

/**
 * @typedef {"get"|"create"|"update"|"delete"} DatabaseAction
 */

/**
 * @typedef {Exclude<DatabaseAction, "get">} DatabaseWriteAction
 */

/**
 * @typedef DatabaseGetOperation
 * @property {Record<string, any>} query        A query object which identifies the set of Documents retrieved
 * @property {"get"} action                     The action of this database operation
 * @property {string} documentName              The Document name
 * @property {false} [broadcast]                Get requests are never broadcast
 * @property {boolean} [index]                  Return indices only instead of full Document records
 * @property {string[]} [indexFields]           An array of field identifiers which should be indexed
 * @property {string|null} [pack=null]          A compendium collection ID which contains the Documents
 * @property {Document|null} [parent=null]      A parent Document within which Documents are embedded
 * @property {string} [parentUuid]              A parent Document UUID provided when the parent instance is unavailable
 * @property {object} [queryOptions]            Additional options passed to the ServerDocument#find API
 */

/**
 * @typedef DatabaseCreateOperation
 * @property {boolean} broadcast                Whether the database operation is broadcast to other connected clients
 * @property {"create"} action                  The action of this database operation
 * @property {string} documentName              The Document name
 * @property {object[]} data                    An array of data objects from which to create Documents
 * @property {boolean} [keepId=false]           Retain the _id values of provided data instead of generating new ids
 * @property {boolean} [keepEmbeddedIds=true]   Retain the _id values of embedded document data instead of generating
 *                                              new ids for each embedded document
 * @property {number} [modifiedTime]            The timestamp when the operation was performed
 * @property {boolean} [noHook=false]           Skip dispatch of preCreate hooks for this operation
 * @property {boolean} [render=true]            Re-render Applications whose display depends on the created Documents
 * @property {boolean} [controlObject=false]    Control the object of any created Documents
 * @property {boolean} [renderSheet=false]      Render the sheet Application for any created Documents
 * @property {Document|null} [parent=null]      A parent Document within which Documents are embedded
 * @property {string|null} pack                 A compendium collection ID which contains the Documents
 * @property {string|null} [parentUuid]         A parent Document UUID provided when the parent instance is unavailable
 * @property {boolean} [dryRun]                 Is the operation a dry run? If so, an empty result array is returned
 *                                              before the Documents are created.
 * @property {Record<string, object>} [_createData] Used internally by server-side backend
 * @property {(string|object)[]} [_result]      Used internally by the server-side backend
 * @property {Record<string, string>} [extractedImages] Base64 images extracted during server-side processing
 */

/**
 * @typedef DatabaseUpdateOperation
 * @property {boolean} broadcast                Whether the database operation is broadcast to other connected clients
 * @property {"update"} action                  The action of this database operation
 * @property {string} documentName              The Document name
 * @property {object[]} updates                 An array of data objects used to update existing Documents.
 *                                              Each update object must contain the _id of the target Document
 * @property {boolean} [diff=true]              Difference each update object against current Document data and only use
 *                                              differential data for the update operation
 * @property {number} [modifiedTime]            The timestamp when the operation was performed
 * @property {boolean} [recursive=true]         Merge objects recursively. If false, inner objects will be replaced
 *                                              explicitly. Use with caution!
 * @property {boolean} [render=true]            Re-render Applications whose display depends on the created Documents
 * @property {boolean} [noHook=false]           Skip dispatch of preUpdate hooks for this operation
 * @property {Document|null} [parent=null]      A parent Document within which Documents are embedded
 * @property {string|null} pack                 A compendium collection ID which contains the Documents
 * @property {string|null} [parentUuid]         A parent Document UUID provided when the parent instance is unavailable
 * @property {Record<string, object>} [_updateData] Used internally by the server-side backend
 * @property {(string|object)[]} [_result]      Used internally by the server-side backend
 * @property {boolean} [dryRun]                 Is the operation a dry run? If so, an empty result array is returned
 *                                              before the Documents are updated.
 * @property {Record<string, string>} [extractedImages] Base64 images extracted during server-side processing
 */

/**
 * @typedef DatabaseDeleteOperation
 * @property {boolean} broadcast                Whether the database operation is broadcast to other connected clients
 * @property {"delete"} action                  The action of this database operation
 * @property {string} documentName              The Document name
 * @property {string[]} ids                     An array of Document ids which should be deleted
 * @property {boolean} [deleteAll=false]        Delete all documents in the Collection, regardless of _id
 * @property {Record<string, string>} [replacements] The mapping of IDs of deleted Documents to the UUIDs of the
 *                                              Documents that replace the deleted Documents
 * @property {number} [modifiedTime]            The timestamp when the operation was performed
 * @property {boolean} [noHook=false]           Skip dispatch of preDelete hooks for this operation
 * @property {boolean} [render=true]            Re-render Applications whose display depends on the deleted Documents
 * @property {Document|null} [parent=null]      A parent Document within which Documents are embedded
 * @property {string|null} pack                 A compendium collection ID which contains the Documents
 * @property {string|null} [parentUuid]         A parent Document UUID provided when the parent instance is unavailable
 * @property {boolean} [dryRun]                 Is the operation a dry run? If so, an empty result array is returned
 *                                              before the Documents are deleted.
 * @property {(string|object)[]} [_result]      An alias for 'ids' used internally by the server-side backend
 */

/**
 * @typedef {DatabaseGetOperation|DatabaseCreateOperation|DatabaseUpdateOperation
 *   |DatabaseDeleteOperation} DatabaseOperation
 */

/**
 * @typedef {Exclude<DatabaseOperation, DatabaseGetOperation>} DatabaseWriteOperation
 */

/**
 * @typedef DocumentSocketRequest
 * @property {string} type                      The type of Document being transacted
 * @property {DatabaseAction} action            The action of the request
 * @property {DatabaseOperation} operation      Operation parameters for the request
 * @property {string} userId                    The id of the requesting User
 * @property {boolean} broadcast                Should the response be broadcast to other connected clients?
 */

/**
 * @typedef DataModelFromSourceOptions
 * @property {boolean} [strict=false]    Models created from trusted source data are validated non-strictly.
 *                                       Default: `false`.
 */

/**
 * @typedef DocumentCloneOptions
 * @property {boolean} [save=false]             Save the clone to the World database? Default: `false`.
 * @property {boolean} [keepId=false]           Keep the same ID of the original document. Default: `false`.
 * @property {boolean} [addSource=false]        Track the clone source. Default: `false`.
 * @property {boolean} [discardInvalidEmbedded=false]  Discard invalid embedded documents from the source.
 *                                                     Not to be confused with `dropInvalidEmbedded`, which
 *                                                     does not discard the invalid embedded documents but
 *                                                     just prevents the document from being considered invalid
 *                                                     and drops invalid embedded documents during preparation.
 *                                                     Default: `false`.
 */

/**
 * @callback DocumentPermissionTest
 * @param {BaseUser} user         The User attempting the operation
 * @param {Document} document     The Document being operated upon
 * @param {object} [data]         Data provided to a creation or update operation
 * @returns {boolean}
 */

/**
 * @typedef DocumentClassMetadata
 * @property {string} name
 * @property {string} label
 * @property {string[]} coreTypes
 * @property {string} collection
 * @property {Record<string, string>} embedded
 * @property {Record<
 *   "view"|"create"|"update"|"delete", keyof USER_ROLES|keyof DOCUMENT_OWNERSHIP_LEVELS|DocumentPermissionTest
 * >} permissions
 * @property {boolean} hasTypeData
 * @property {boolean} [baseTypeAllowed] If the Document class has type data, can users normally create instances of the
 *                                       "base" type?
 * @property {boolean} indexed
 * @property {string[]} compendiumIndexFields
 * @property {string[]} preserveOnImport
 * @property {string} [schemaVersion]
 */

/**
 * @callback RecursiveFieldClearCallback
 * @param {object} data       The (partial) Document data.
 * @param {string} fieldName  The name of the field to clear.
 * @returns {void}
 */
