import crypto from 'crypto';
import { decrypt, encrypt } from './auth/crypto.js';
import { APIKeyAuthentication } from './auth/strategies/apiKey.js';
import { JWTAuthentication } from './auth/strategies/jwt.js';
import localOperations from './collections/operations/local/index.js';
import { validateSchema } from './config/validate.js';
import { consoleEmailAdapter } from './email/consoleEmailAdapter.js';
import { fieldAffectsData } from './fields/config/types.js';
import localGlobalOperations from './globals/operations/local/index.js';
import flattenFields from './utilities/flattenTopLevelFields.js';
import Logger from './utilities/logger.js';
import { serverInit as serverInitTelemetry } from './utilities/telemetry/events/serverInit.js';
/**
 * @description Payload
 */ export class BasePayload {
    /**
   * @description Authorization and Authentication using headers and cookies to run auth user strategies
   * @returns cookies: Map<string, string>
   * @returns permissions: Permissions
   * @returns user: User
   */ auth = async (options)=>{
        const { auth } = localOperations.auth;
        return auth(this, options);
    };
    authStrategies;
    collections = {};
    config;
    /**
   * @description Performs count operation
   * @param options
   * @returns count of documents satisfying query
   */ count = async (options)=>{
        const { count } = localOperations;
        return count(this, options);
    };
    /**
   * @description Performs create operation
   * @param options
   * @returns created document
   */ create = async (options)=>{
        const { create } = localOperations;
        return create(this, options);
    };
    db;
    decrypt = decrypt;
    duplicate = async (options)=>{
        const { duplicate } = localOperations;
        return duplicate(this, options);
    };
    email;
    // TODO: re-implement or remove?
    // errorHandler: ErrorHandler
    encrypt = encrypt;
    extensions;
    /**
   * @description Find documents with criteria
   * @param options
   * @returns documents satisfying query
   */ find = async (options)=>{
        const { find } = localOperations;
        return find(this, options);
    };
    /**
   * @description Find document by ID
   * @param options
   * @returns document with specified ID
   */ findByID = async (options)=>{
        const { findByID } = localOperations;
        return findByID(this, options);
    };
    findGlobal = async (options)=>{
        const { findOne } = localGlobalOperations;
        return findOne(this, options);
    };
    /**
   * @description Find global version by ID
   * @param options
   * @returns global version with specified ID
   */ findGlobalVersionByID = async (options)=>{
        const { findVersionByID } = localGlobalOperations;
        return findVersionByID(this, options);
    };
    /**
   * @description Find global versions with criteria
   * @param options
   * @returns versions satisfying query
   */ findGlobalVersions = async (options)=>{
        const { findVersions } = localGlobalOperations;
        return findVersions(this, options);
    };
    /**
   * @description Find version by ID
   * @param options
   * @returns version with specified ID
   */ findVersionByID = async (options)=>{
        const { findVersionByID } = localOperations;
        return findVersionByID(this, options);
    };
    /**
   * @description Find versions with criteria
   * @param options
   * @returns versions satisfying query
   */ findVersions = async (options)=>{
        const { findVersions } = localOperations;
        return findVersions(this, options);
    };
    forgotPassword = async (options)=>{
        const { forgotPassword } = localOperations.auth;
        return forgotPassword(this, options);
    };
    getAPIURL = ()=>`${this.config.serverURL}${this.config.routes.api}`;
    getAdminURL = ()=>`${this.config.serverURL}${this.config.routes.admin}`;
    globals;
    logger;
    login = async (options)=>{
        const { login } = localOperations.auth;
        return login(this, options);
    };
    resetPassword = async (options)=>{
        const { resetPassword } = localOperations.auth;
        return resetPassword(this, options);
    };
    /**
   * @description Restore global version by ID
   * @param options
   * @returns version with specified ID
   */ restoreGlobalVersion = async (options)=>{
        const { restoreVersion } = localGlobalOperations;
        return restoreVersion(this, options);
    };
    /**
   * @description Restore version by ID
   * @param options
   * @returns version with specified ID
   */ restoreVersion = async (options)=>{
        const { restoreVersion } = localOperations;
        return restoreVersion(this, options);
    };
    schema;
    secret;
    sendEmail;
    types;
    unlock = async (options)=>{
        const { unlock } = localOperations.auth;
        return unlock(this, options);
    };
    updateGlobal = async (options)=>{
        const { update } = localGlobalOperations;
        return update(this, options);
    };
    validationRules;
    verifyEmail = async (options)=>{
        const { verifyEmail } = localOperations.auth;
        return verifyEmail(this, options);
    };
    versions = {};
    delete(options) {
        const { deleteLocal } = localOperations;
        return deleteLocal(this, options);
    }
    /**
   * @description Initializes Payload
   * @param options
   */ async init(options) {
        if (!options?.config) {
            throw new Error('Error: the payload config is required to initialize payload.');
        }
        this.logger = Logger('payload', options.loggerOptions, options.loggerDestination);
        this.config = await options.config;
        if (process.env.NODE_ENV !== 'production') {
            validateSchema(this.config, this.logger);
        }
        if (!this.config.secret) {
            throw new Error('Error: missing secret key. A secret key is needed to secure Payload.');
        }
        this.secret = crypto.createHash('sha256').update(this.config.secret).digest('hex').slice(0, 32);
        this.globals = {
            config: this.config.globals
        };
        this.config.collections.forEach((collection)=>{
            const customID = flattenFields(collection.fields).find((field)=>fieldAffectsData(field) && field.name === 'id');
            let customIDType;
            if (customID?.type === 'number' || customID?.type === 'text') customIDType = customID.type;
            this.collections[collection.slug] = {
                config: collection,
                customIDType
            };
        });
        this.db = this.config.db.init({
            payload: this
        });
        this.db.payload = this;
        if (this.db?.init) {
            await this.db.init();
        }
        if (!options.disableDBConnect && this.db.connect) {
            await this.db.connect();
        }
        // Load email adapter
        if (this.config.email instanceof Promise) {
            const awaitedAdapter = await this.config.email;
            this.email = awaitedAdapter({
                payload: this
            });
        } else if (this.config.email) {
            this.email = this.config.email({
                payload: this
            });
        } else {
            this.logger.warn(`No email adapter provided. Email will be written to console. More info at https://payloadcms.com/docs/email/overview.`);
            this.email = consoleEmailAdapter({
                payload: this
            });
        }
        this.sendEmail = this.email['sendEmail'];
        serverInitTelemetry(this);
        // 1. loop over collections, if collection has auth strategy, initialize and push to array
        let jwtStrategyEnabled = false;
        this.authStrategies = this.config.collections.reduce((authStrategies, collection)=>{
            if (collection?.auth) {
                if (collection.auth.strategies.length > 0) {
                    authStrategies.push(...collection.auth.strategies);
                }
                // 2. if api key enabled, push api key strategy into the array
                if (collection.auth?.useAPIKey) {
                    authStrategies.push({
                        name: `${collection.slug}-api-key`,
                        authenticate: APIKeyAuthentication(collection)
                    });
                }
                // 3. if localStrategy flag is true
                if (!collection.auth.disableLocalStrategy && !jwtStrategyEnabled) {
                    jwtStrategyEnabled = true;
                }
            }
            return authStrategies;
        }, []);
        // 4. if enabled, push jwt strategy into authStrategies last
        if (jwtStrategyEnabled) {
            this.authStrategies.push({
                name: 'local-jwt',
                authenticate: JWTAuthentication
            });
        }
        if (!options.disableOnInit) {
            if (typeof options.onInit === 'function') await options.onInit(this);
            if (typeof this.config.onInit === 'function') await this.config.onInit(this);
        }
        return this;
    }
    update(options) {
        const { update } = localOperations;
        return update(this, options);
    }
}
const initialized = new BasePayload();
export default initialized;
let cached = global._payload;
if (!cached) {
    // eslint-disable-next-line no-multi-assign
    cached = global._payload = {
        payload: null,
        promise: null
    };
}
export const getPayload = async (options)=>{
    if (!options?.config) {
        throw new Error('Error: the payload config is required for getPayload to work.');
    }
    if (cached.payload) {
        return cached.payload;
    }
    if (!cached.promise) {
        cached.promise = new BasePayload().init(options);
    }
    try {
        cached.payload = await cached.promise;
    } catch (e) {
        cached.promise = null;
        throw e;
    }
    return cached.payload;
};

//# sourceMappingURL=index.js.map