/*!
 * Copyright (C) Microsoft Corporation. All rights reserved.
 */
import { getDataOperationExecutor } from '../../../../data/powerAppsData';
import { DataSources } from '../common/types';
import { createErrorResponse, DataOperationErrorMessages } from '../error/error';
/**
 * RuntimeDataOperations provides a unified interface for performing data operations
 * across different data sources.
 * @private
 */
export class DefaultDataOperationOrchestrator {
    _dataverseOperation;
    _connectorOperation;
    _connectionsService;
    // Static identifiers for services and actions
    // Used to identify specific services and actions within the PowerApps environment
    constructor(_dataverseOperation, _connectorOperation, _connectionsService) {
        this._dataverseOperation = _dataverseOperation;
        this._connectorOperation = _connectorOperation;
        this._connectionsService = _connectionsService;
    }
    /**
     * Creates a new record in the specified data source.
     * @param tableName - The name of the table.
     * @param data - The record data to create.
     * @returns A promise that resolves to the operation result.
     * @throws DataOperationError if the operation fails.
     */
    async createRecordAsync(tableName, data) {
        try {
            this._validateParams({ tableName, data });
            const executor = await this._getExecutor(tableName);
            return await executor.createRecordAsync(tableName, data);
        }
        catch (error) {
            return createErrorResponse(error, 'Create record operation failed');
        }
    }
    /**
     * Updates an existing record in the specified data source.
     * @param tableName - The name of the table.
     * @param id - The ID of the record to update.
     * @param data - The updated record data.
     * @returns A promise that resolves to the operation result.
     * @throws DataOperationError if the operation fails.
     */
    async updateRecordAsync(tableName, id, data) {
        try {
            this._validateParams({ tableName, id, data });
            const executor = await this._getExecutor(tableName);
            return await executor.updateRecordAsync(tableName, id, data);
        }
        catch (error) {
            return createErrorResponse(error, 'Update record operation failed');
        }
    }
    /**
     * Uploads data for an existing record in the specified data source.
     * @param tableName - The name of the table.
     * @param id - The ID of the record to upload data for.
     * @param columnName - The name of the file column to upload to.
     * @param fileName - The name of the file to upload (used for Dataverse file columns).
     * @param data - The data to upload.
     * @returns A promise that resolves to the operation result.
     * @throws DataOperationError if the operation fails.
     */
    async uploadFileToRecord(tableName, id, columnName, fileName, data) {
        try {
            this._validateParams({ tableName, id, columnName, fileName, data });
            const executor = await this._getExecutor(tableName);
            return await executor.uploadFileToRecord(tableName, id, columnName, fileName, data);
        }
        catch (error) {
            return createErrorResponse(error, 'Upload record operation failed');
        }
    }
    /**
     * Downloads binary data from a file column in the specified data source.
     * @param tableName - The name of the table.
     * @param id - The ID of the record to download from.
     * @param columnName - The name of the file column to download from.
     * @returns A promise that resolves to the operation result with file contents as binary data (Uint8Array).
     * @throws DataOperationError if the operation fails.
     */
    async downloadFileFromRecord(tableName, id, columnName) {
        try {
            this._validateParams({ tableName, id, columnName });
            const executor = await this._getExecutor(tableName);
            return await executor.downloadFileFromRecord(tableName, id, columnName);
        }
        catch (error) {
            return createErrorResponse(error, 'Download record operation failed');
        }
    }
    /**
     * Downloads binary image data from an image column in the specified data source.
     * Only applicable to Dataverse data sources.
     * @param tableName - The logical name of the Dataverse table.
     * @param recordId - The ID of the record containing the image.
     * @param columnName - The name of the image column to download from.
     * @param fullSize - When true, requests the full-size image (`?size=full`). Defaults to false.
     * @returns A promise that resolves to the operation result with image contents as binary data (Uint8Array).
     */
    async downloadImageFromRecord(tableName, recordId, columnName, fullSize) {
        try {
            this._validateParams({ tableName, recordId, columnName });
            const executor = await this._getExecutor(tableName);
            return await executor.downloadImageFromRecord(tableName, recordId, columnName, fullSize);
        }
        catch (error) {
            return createErrorResponse(error, 'Download image operation failed');
        }
    }
    /**
     * Deletes the file or image data stored in a file/image column of an existing record.
     * Only applicable to Dataverse data sources.
     * @param tableName - The logical name of the Dataverse table.
     * @param recordId - The ID of the record containing the file or image.
     * @param columnName - The name of the file or image column whose data should be deleted.
     * @returns A promise that resolves to the operation result.
     */
    async deleteFileOrImageFromRecord(tableName, recordId, columnName) {
        try {
            this._validateParams({ tableName, recordId, columnName });
            const executor = await this._getExecutor(tableName);
            return await executor.deleteFileOrImageFromRecord(tableName, recordId, columnName);
        }
        catch (error) {
            return createErrorResponse(error, 'Delete file or image operation failed');
        }
    }
    /**
     * Deletes a record from the specified data source.
     * @param tableName - The name of the table.
     * @param id - The ID of the record to delete.
     * @returns A promise that resolves to the operation result.
     * @throws DataOperationError if the operation fails.
     */
    async deleteRecordAsync(tableName, id) {
        try {
            this._validateParams({ tableName, id });
            const executor = await this._getExecutor(tableName);
            return await executor.deleteRecordAsync(tableName, id);
        }
        catch (error) {
            return createErrorResponse(error, 'Delete record operation failed');
        }
    }
    /**
     * Retrieves a record from the specified data source.
     * @param tableName - The name of the table.
     * @param id - The ID of the record to retrieve.
     * @param options - Optional operation options.
     * @returns A promise that resolves to the operation result.
     * @throws DataOperationError if the operation fails.
     */
    async retrieveRecordAsync(tableName, id, options) {
        try {
            this._validateParams({ tableName, id });
            const executor = await this._getExecutor(tableName);
            this._validateOptions(options);
            return await executor.retrieveRecordAsync(tableName, id, options);
        }
        catch (error) {
            return createErrorResponse(error, 'Retrieve record operation failed');
        }
    }
    /**
     * Retrieves multiple records from the specified data source.
     * @param tableName - The name of the table.
     * @param options - Optional operation options.
     * @returns A promise that resolves to the operation result.
     * @throws DataOperationError if the operation fails.
     */
    async retrieveMultipleRecordsAsync(tableName, options) {
        try {
            this._validateParams({ tableName });
            const executor = await this._getExecutor(tableName);
            this._validateOptions(options);
            return await executor.retrieveMultipleRecordsAsync(tableName, options);
        }
        catch (error) {
            return createErrorResponse(error, 'Retrieve multiple records operation failed');
        }
    }
    /**
     * Executes a data operation on the specified data source.
     * @param operation - The operation to execute
     * @returns A promise that resolves to the operation result.
     * @throws DataOperationError if the operation fails.
     */
    async executeAsync(operation) {
        try {
            this._validateParams({ operation });
            const executor = await this._getExecutor('', operation.connectorOperation ? DataSources.Connector : DataSources.Dataverse);
            return await executor.executeAsync(operation);
        }
        catch (error) {
            return createErrorResponse(error, 'Execute operation failed');
        }
    }
    /**
     * Retrieves the appropriate executor based on the data source.
     * @param dataSource - The data source to retrieve the executor for.
     * @returns The corresponding executor instance.
     * @throws DataOperationError if the data source is invalid.
     * // TODO: Add Dataverse support
     */
    async _getExecutor(tableName, dataSource) {
        const dataOperationExecutorOverride = getDataOperationExecutor();
        if (dataOperationExecutorOverride) {
            return dataOperationExecutorOverride;
        }
        const dataSourceType = dataSource || (await this._connectionsService.getDataSource(tableName)).dataSourceType;
        switch (dataSourceType) {
            case DataSources.Dataverse:
                return this._dataverseOperation;
            case DataSources.Connector:
                return this._connectorOperation;
            default:
                return this._connectorOperation;
        }
    }
    /**
     * Validates the input parameters for data operations.
     * @param params - The parameters to validate.
     * @throws DataOperationError if validation fails.
     */
    _validateParams(params) {
        for (const [key, value] of Object.entries(params)) {
            if (!value) {
                throw new Error(`${DataOperationErrorMessages.InvalidOperationParameters}: ${key} is required`);
            }
        }
    }
    /**
     * Validates the operation options.
     * @param options - The operation options to validate.
     * @throws Error if validation fails.
     */
    _validateOptions(options) {
        if (!options) {
            return;
        }
        // maxPageSize must be a number
        if (options.maxPageSize && typeof options.maxPageSize !== 'number') {
            throw new Error(`${DataOperationErrorMessages.InvalidOperationParameters}: maxPageSize must be a number`);
        }
        /*
        select
        - must be an array of string
        - no empty strings allowed
        - no null or undefined values allowed
        */
        if (options.select) {
            if (!Array.isArray(options.select)) {
                throw new Error(`${DataOperationErrorMessages.InvalidOperationParameters}: select must be an array of strings`);
            }
            if (options.select.some((s) => typeof s !== 'string' || s.trim() === '')) {
                throw new Error(`${DataOperationErrorMessages.InvalidOperationParameters}: select must contain only non-empty strings`);
            }
        }
        // filter must be a string
        if (options.filter && typeof options.filter !== 'string') {
            throw new Error(`${DataOperationErrorMessages.InvalidOperationParameters}: filter must be a string`);
        }
        // orderBy must be an array of strings
        if (options.orderBy) {
            if (!Array.isArray(options.orderBy)) {
                throw new Error(`${DataOperationErrorMessages.InvalidOperationParameters}: orderBy must be an array of strings`);
            }
            if (options.orderBy.some((s) => typeof s !== 'string' || s.trim() === '')) {
                throw new Error(`${DataOperationErrorMessages.InvalidOperationParameters}: orderBy must contain only non-empty strings`);
            }
        }
        // top must be a number
        if (options.top && typeof options.top !== 'number') {
            throw new Error(`${DataOperationErrorMessages.InvalidOperationParameters}: top must be a number`);
        }
        // skip must be a number
        if (options.skip && typeof options.skip !== 'number') {
            throw new Error(`${DataOperationErrorMessages.InvalidOperationParameters}: skip must be a number`);
        }
        // count must be a boolean
        if (options.count && typeof options.count !== 'boolean') {
            throw new Error(`${DataOperationErrorMessages.InvalidOperationParameters}: count must be a boolean`);
        }
    }
}
//# sourceMappingURL=defaultOperationOrchestrator.js.map