import type { ModelWithSource } from './models';
import type { DocumentWithSource } from './content-source-document';
import type {
    DocumentModelField,
    DocumentObjectField,
    DocumentFieldNonLocalizedForType
} from './content-source-document-fields';
import type { FieldType } from './model-fields';
import type { SchemaWithSource } from './content-source';
import { Logger } from './logger';

export interface ConfigDelegate {
    /**
     * Returns a {@link import('./content-source-document').Document} by ID.
     *
     * If only one document with the provided `id` was found among all the content
     * sources, that document will be returned. If more than one document was found,
     * a warning message will be printed to the console and 'undefined' will be
     * returned. In this case, provide 'srcType' and/or 'srcProjectId' to narrow
     * down the document search.
     *
     * @param {object} options
     * @param {string} options.id The ID of the document
     * @param {string} [options.srcType] The content source type containing the document
     * @param {string} [options.srcProjectId] The content source project ID containing the document
     */
    getDocumentById: (options: {
        id: string;
        srcType?: string;
        srcProjectId?: string;
    }) => DocumentWithSource | undefined;

    /**
     * Returns documents of all content sources, or documents of a specific
     * content source if `srcType` and/or `srcProjectId` were provided.
     *
     * @param {object} options
     * @param {string} [options.srcType] The content source type
     * @param {string} [options.srcProjectId] The content source project ID
     */
    getDocuments: (options?: { srcType?: string; srcProjectId?: string }) => DocumentWithSource[];

    /**
     * Return a {@link Model} by name.
     *
     * If only one model with the provided `modelName` was found among all the
     * content sources, that model is returned. If more than one model was found,
     * a warning message will be printed to the console and 'undefined' will be
     * returned. In this case, provide 'srcType' and/or 'srcProjectId' to narrow
     * down the model search.
     *
     * @param {object} options
     * @param {string} options.modelName The name of the model
     * @param {string} [options.srcType] The content source type containing the model
     * @param {string} [options.srcProjectId] The content source project ID containing the model
     */
    getModelByName: (options: {
        modelName: string;
        srcType?: string;
        srcProjectId?: string;
    }) => ModelWithSource | undefined;

    /**
     * Returns the schemas of all content sources, or a schema of a specific
     * content source if `srcType` and/or `srcProjectId` were provided.
     *
     * @param {object} options
     * @param {string} [options.srcType] The content source type
     * @param {string} [options.srcProjectId] The content source project ID
     */
    getSchemas: (options?: { srcType?: string; srcProjectId?: string }) => SchemaWithSource[];

    /**
     * Returns the default locale of a content source.
     *
     * @param options
     * @param {string} options.srcType The content source type containing the model
     * @param {string} [options.srcProjectId] The content source project ID containing the model
     */
    getDefaultLocaleBySource: (options: { srcType: string; srcProjectId?: string }) => string | undefined;

    /**
     * Returns a document field for the provided `fieldPath`.
     *
     * The `fieldPath` should be a string specifying a "dot-notation" path to
     * a nested field within the provided `document`. The `fieldPath` can contain
     * field names of nested objects, referenced documents and their nested
     * objects, and numbers specifying indexes of array items.
     *
     * An optional `fromField` can be provided to begin resolving the `fieldPath`
     * from a nested document field. The `fromField` must be a nested field within
     * the specified `document`.
     *
     * An optional `locale` can be provided to resolve localized fields. If no
     * `locale` is provided, the default locale for the document's content
     * source is assumed.
     *
     * An optional generic value can be provided to specify the expected type
     * of the returned field.
     *
     * @example
     * getDocumentFieldForFieldPath<'string'>({
     *     document,
     *     fieldPath: 'author.firstName'
     * })
     *
     * @param {object} options
     * @param {DocumentWithSource} options.document The document from which to
     *   begin resolving the `fieldPath`
     * @param {string} options.fieldPath The `fieldPath` relative to the `document`,
     *   or relative to the `fromField` if specified.
     * @param {DocumentModelField | DocumentObjectField} [options.fromField]
     *   A nested field within the provided `document` from which to begin resolving the `fieldPath`.
     * @param {string} [options.locale] The locale to use when resolving localized fields.
     */
    getDocumentFieldForFieldPath: <Type extends FieldType>(options: {
        document: DocumentWithSource;
        fromField?: DocumentModelField | DocumentObjectField;
        fieldPath: string;
        locale?: string;
    }) => DocumentFieldNonLocalizedForType<Type> | undefined;

    /**
     * Get a logger instance used to print messages to the console that appear locally and when running on Stackbit.
     */
    getLogger: () => Logger;
}
