import { nanoid } from 'nanoid';
import { Model } from '../core/Model.js';
import { exponentialBackoffDelay, shouldRetryApiError, retry } from './retry_logic.js';
import { fromError } from '../exceptions/builder.js';
import { Exception } from '@api-client/core/exceptions/exception.js';
/**
 * TODO: add caching of the user list. When querying for users, each user should be cached
 *       and the next time the user is queried, it should be fetched from the cache.
 *       This will prevent multiple requests to the server for the same user.
 *       This should also work when fetching a list of users by their IDs.
 *       Cached users should not be queried again.
 *       Note, the cache should have a TTL of 5 minutes or so, to ensure that the user list is not stale.
 * @fires users - Fired when the list of users is updated.
 */
export default class FileShareModel extends Model {
    /**
     * The organization ID.
     */
    oid;
    /**
     * The list of file ids to process in this request.
     */
    ids;
    /**
     * A reference to the SDK service.
     */
    sdk;
    /**
     * A flag indicating whether the model is currently loading data.
     */
    loading;
    constructor(sdk) {
        super();
        this.sdk = sdk;
        this.loading = false;
    }
    async read(oid, ids) {
        this.oid = oid;
        this.ids = ids;
        this.debounce(() => this.#read());
        await this.taskComplete;
        return this.value || [];
    }
    async #read() {
        const { ids, oid } = this;
        if (!ids || !oid) {
            throw new Exception('FileShareModel: oid and ids must be set before reading files.');
        }
        const retryOptions = {
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
            retries: this.retryCount,
        };
        const operation = async () => {
            return this.sdk.file.readBulk(oid, ids);
        };
        this.loading = true;
        try {
            const info = await retry(operation, retryOptions);
            const valid = info.items.filter((item) => {
                if (!item) {
                    return false;
                }
                if (!item.capabilities) {
                    return false;
                }
                return item.capabilities.canShare;
            });
            if (valid.length !== ids.length) {
                const missing = ids.filter((id) => !valid.some((file) => file.key === id));
                throw new Exception(`Some files were not found: ${missing.join(', ')}`);
            }
            this.raw = valid;
        }
        catch (e) {
            const err = fromError(e);
            this.notifyError(err);
            throw err;
        }
        finally {
            this.loading = false;
        }
    }
    /**
     * Lists permissions that are common across multiple files.
     * A permission is considered common if, for a given `granteeId`, the same `type` and `role`
     * combination exists in every file provided. The returned permissions are the
     * actual `IPermission` instances from the first file in the input array that meet this criterion.
     *
     * @param files An array of `IFile` objects.
     * @returns An array of `IPermission` objects that are common to all provided files.
     *          Returns an empty array if fewer than two files are provided, if the first
     *          file has no permissions, or if no common permissions are found.
     */
    static findCommonPermissions(files) {
        // Common permissions can only be determined if there are at least two files to compare.
        if (!files) {
            return [];
        }
        if (files.length === 1) {
            return [...files[0].permissions];
        }
        const firstFilePermissions = files[0].permissions;
        const commonPermissions = [];
        // Iterate through each permission in the first file.
        // This permission will be our candidate for a common permission.
        for (const permFromFirstFile of firstFilePermissions) {
            let isPermCommonToAllFiles = true;
            // Check this candidate permission against all other files.
            for (let i = 1; i < files.length; i++) {
                const otherFile = files[i];
                const otherFilePermissions = otherFile.permissions;
                // If a subsequent file has no permissions, the candidate cannot be common.
                if (!otherFilePermissions || otherFilePermissions.length === 0) {
                    isPermCommonToAllFiles = false;
                    break; // Move to the next candidate from the first file.
                }
                // Check if the current candidate permission (based on granteeId, type, and role)
                // exists in the `otherFile`.
                const foundInOtherFile = otherFilePermissions.some((otherPerm) => otherPerm.type === permFromFirstFile.type && otherPerm.role === permFromFirstFile.role);
                if (!foundInOtherFile) {
                    isPermCommonToAllFiles = false;
                    break; // Move to the next candidate from the first file.
                }
            }
            // If the candidate permission was found in all other files, add it to the results.
            if (isPermCommonToAllFiles) {
                commonPermissions.push(permFromFirstFile);
            }
        }
        return commonPermissions;
    }
    async searchUsers(ignore, query) {
        this.debounce(() => this.#queryUsers(ignore, query));
    }
    async #queryUsers(ignore, query) {
        const { oid } = this;
        if (!oid) {
            throw new Exception('FileShareModel: oid must be set before listing users.');
        }
        const retryOptions = {
            retries: this.retryCount,
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
        };
        const operation = async () => {
            const options = {
                limit: 20,
            };
            if (query) {
                // options.filter = {
                //   op: 'OR',
                //   filters: [
                //     { field: 'name', operator: 'con', value: query },
                //     { field: 'email', operator: 'con', value: query },
                //   ],
                // }
                options.filter = {
                    op: 'AND',
                    filters: [
                        {
                            field: 'id',
                            operator: 'nin',
                            value: ignore,
                        },
                        {
                            op: 'OR',
                            filters: [
                                { field: 'name', operator: 'con', value: query },
                                { field: 'email', operator: 'con', value: query },
                            ],
                        },
                    ],
                };
            }
            return this.sdk.organizations.users.list(oid, options);
        };
        const result = await retry(operation, retryOptions);
        this.dispatchEvent(new CustomEvent('users', { detail: result.items }));
    }
    /**
     * Fetches a bulk of users by their IDs.
     * @param oid The organization ID.
     * @param ids An array of user IDs to fetch.
     * @returns A promise that resolves to an array of IUser objects.
     */
    async getUsersBulk(oid, ids) {
        if (!oid || !ids) {
            throw new Exception('FileShareModel: oid and ids must be set before reading users.');
        }
        if (ids.length === 0) {
            return [];
        }
        const retryOptions = {
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
            retries: this.retryCount,
        };
        const operation = async () => {
            return this.sdk.organizations.users.readBatch(oid, ids);
        };
        const info = await retry(operation, retryOptions);
        return info.items.filter(Boolean);
    }
    async commit(operations) {
        const { oid, ids } = this;
        if (!oid || !ids || ids.length === 0) {
            throw new Exception('FileShareModel: oid and ids must be set before committing changes.');
        }
        const info = {
            id: nanoid(),
            patch: operations,
        };
        const id = ids[0];
        const retryOptions = {
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
            retries: this.retryCount,
        };
        const operation = async () => {
            return this.sdk.file.patchUsers(oid, id, info);
        };
        this.loading = true;
        try {
            const file = await retry(operation, retryOptions);
            return file;
        }
        catch (e) {
            const err = fromError(e);
            this.notifyError(err);
            throw err;
        }
        finally {
            this.loading = false;
        }
    }
}
//# sourceMappingURL=FileShareModel.js.map