import { Model } from '../core/Model.js';
import { exponentialBackoffDelay, shouldRetryApiError, retry } from './retry_logic.js';
import { fromError } from '../exceptions/builder.js';
import { LiveData } from '../reactive/LiveData.js';
var ModelState;
(function (ModelState) {
    /**
     * Not doing anything.
     */
    ModelState[ModelState["idle"] = 0] = "idle";
    /**
     * Currently querying for file data.
     */
    ModelState[ModelState["reading"] = 1] = "reading";
})(ModelState || (ModelState = {}));
export default class UsersModel extends Model {
    /**
     * The current state of the model.
     */
    state = ModelState.idle;
    /**
     * The organization ID this model is associated with.
     */
    oid;
    /**
     * A reference to the SDK service used to perform API calls.
     */
    sdk;
    /**
     * Pagination information for invitations.
     */
    invitationsInfo;
    /**
     * Pagination information for users.
     */
    usersInfo;
    get loading() {
        return this.state === ModelState.reading;
    }
    get value() {
        return this.raw;
    }
    constructor(oid, sdk) {
        super();
        this.oid = oid;
        this.sdk = sdk;
        this.invitationsInfo = { ended: false };
        this.usersInfo = { ended: false };
        this.raw = new LiveData([]);
    }
    /**
     * Lists users in the organization.
     * It performs a paginated query to the API.
     * Call this method again to fetch the next page of results.
     */
    async list() {
        if (this.state === ModelState.idle) {
            this.debounce(() => this.#fetchPage());
        }
        await this.taskComplete;
    }
    /**
     * Checks if the model has any users.
     * @returns True if there are users, false otherwise.
     */
    hasUsers() {
        return this.value.value.length > 0;
    }
    /**
     * Finds a user by their key.
     */
    find(key) {
        return this.value.value.find((user) => user.key === key);
    }
    /**
     * Adds a user to the model.
     * @param user The user to add to the model.
     */
    add(user) {
        if (this.find(user.key)) {
            throw new Error(`User with key ${user.key} already exists.`);
        }
        this.value.value.push(user);
    }
    /**
     * Replaces an existing user in the model.
     * If the user does not exist, it will be added.
     * @param key The key of the user to remove.
     */
    replace(user) {
        const index = this.value.value.findIndex((u) => u.key === user.key);
        if (index === -1) {
            return this.add(user);
        }
        this.value.value[index] = user;
    }
    /**
     * Removes a user from the model.
     * @param key The key of the user to remove.
     */
    remove(key) {
        const index = this.value.value.findIndex((user) => user.key === key);
        if (index === -1) {
            return;
        }
        this.value.value.splice(index, 1);
    }
    async #fetchPage() {
        if (this.invitationsInfo.ended && this.usersInfo.ended) {
            return;
        }
        this.state = ModelState.reading;
        try {
            await this.#fetchUsers();
            await this.#fetchInvitations();
        }
        catch (e) {
            const err = fromError(e);
            this.notifyError(err);
            throw err;
        }
        finally {
            this.state = ModelState.idle;
        }
    }
    async #fetchUsers() {
        const operation = async () => {
            const options = {
                limit: this.limit,
                sort: 'name',
            };
            if (this.usersInfo.cursor) {
                options.cursor = this.usersInfo.cursor;
            }
            return this.sdk.organizations.users.list(this.oid, options);
        };
        const retryOptions = {
            retries: this.retryCount,
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
        };
        const users = await retry(operation, retryOptions);
        this.value.set(this.value.value.concat(users.items));
        this.usersInfo.cursor = users.cursor;
        if (users.items.length < this.limit) {
            this.usersInfo.ended = true;
        }
    }
    async #fetchInvitations() {
        const operation = async () => {
            const options = {
                limit: this.limit,
            };
            if (this.usersInfo.cursor) {
                options.cursor = this.usersInfo.cursor;
            }
            return this.sdk.organizations.invitations.list(this.oid);
        };
        const retryOptions = {
            retries: this.retryCount,
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
        };
        const invitations = await retry(operation, retryOptions);
        this.value.set(this.value.value.concat(invitations.items));
        this.usersInfo.cursor = invitations.cursor;
        if (invitations.items.length < this.limit) {
            this.usersInfo.ended = true;
        }
    }
    async deactivate(uid) {
        const { oid } = this;
        if (!uid) {
            throw new Error('User ID (uid) is not set. Cannot deactivate user.');
        }
        this.state = ModelState.reading;
        const operation = async () => {
            return this.sdk.organizations.users.deactivate(oid, uid);
        };
        const retryOptions = {
            retries: this.retryCount,
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
        };
        try {
            const updated = await retry(operation, retryOptions);
            this.replace(updated);
        }
        catch (e) {
            const err = fromError(e);
            this.notifyError(err);
            throw err;
        }
        finally {
            this.state = ModelState.idle;
        }
    }
    async activate(uid) {
        const { oid } = this;
        if (!uid) {
            throw new Error('User ID (uid) is not set. Cannot activate user.');
        }
        this.state = ModelState.reading;
        const operation = async () => {
            return this.sdk.organizations.users.activate(oid, uid);
        };
        const retryOptions = {
            retries: this.retryCount,
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
        };
        try {
            const updated = await retry(operation, retryOptions);
            this.replace(updated);
        }
        catch (e) {
            const err = fromError(e);
            this.notifyError(err);
            throw err;
        }
        finally {
            this.state = ModelState.idle;
        }
    }
    async deleteUser(uid) {
        const { oid } = this;
        if (!uid) {
            throw new Error('User ID (uid) is not set. Cannot delete user.');
        }
        this.state = ModelState.reading;
        const operation = async () => {
            return this.sdk.organizations.users.delete(oid, uid);
        };
        const retryOptions = {
            retries: this.retryCount,
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
        };
        try {
            await retry(operation, retryOptions);
            this.remove(uid);
        }
        catch (e) {
            const err = fromError(e);
            this.notifyError(err);
            throw err;
        }
        finally {
            this.state = ModelState.idle;
        }
    }
    async deleteInvitation(key) {
        const { oid } = this;
        if (!key) {
            throw new Error('Invitation key is not set. Cannot delete invitation.');
        }
        this.state = ModelState.reading;
        const operation = async () => {
            await this.sdk.organizations.invitations.delete(oid, key);
        };
        const retryOptions = {
            retries: this.retryCount,
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
        };
        try {
            await retry(operation, retryOptions);
            this.remove(key);
        }
        catch (e) {
            const err = fromError(e);
            this.notifyError(err);
            throw err;
        }
        finally {
            this.state = ModelState.idle;
        }
    }
    async resendInvitation(key) {
        const { oid } = this;
        if (!key) {
            throw new Error('Invitation key is not set. Cannot resend invitation.');
        }
        this.state = ModelState.reading;
        const operation = async () => {
            return this.sdk.organizations.invitations.resend(oid, key);
        };
        const retryOptions = {
            retries: this.retryCount,
            delayMs: exponentialBackoffDelay,
            shouldRetry: shouldRetryApiError,
        };
        try {
            const updated = await retry(operation, retryOptions);
            this.replace(updated);
        }
        catch (e) {
            const err = fromError(e);
            this.notifyError(err);
            throw err;
        }
        finally {
            this.state = ModelState.idle;
        }
    }
}
//# sourceMappingURL=UsersModel.js.map