import { SdkBase, E_RESPONSE_STATUS, E_RESPONSE_NO_VALUE, E_INVALID_JSON, E_RESPONSE_UNKNOWN, } from './SdkBase.js';
import { RouteBuilder } from './RouteBuilder.js';
import { nanoid } from '../nanoid.js';
import { Folder } from '../models/Folder.js';
import { Exception } from '../exceptions/exception.js';
/**
 * In the store, the file is represented by the meta and the media.
 *
 * The meta is used to represent the file in the UI when listing files
 * or presenting their metadata (like name, last modified, etc).
 * The media is the actual contents of the file and is only used by application specializing in this file modification.
 */
export class FilesSdk extends SdkBase {
    /**
     * Lists files (organizations, projects, etc) in the store.
     *
     * @param organization the parent organization for files.
     * @param kinds Optional list of kinds to limit the file types in the result. organizations are always included.
     * @param options Optional query options.
     * @param request Optional request options.
     */
    async list(organization, options = {}, request = {}) {
        const { token } = request;
        const opts = { ...options };
        const url = this.sdk.getUrl(RouteBuilder.files(organization));
        this.sdk.appendListOptions(url, opts);
        const result = await this.sdk.http.get(url.toString(), { token });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to list files. ';
        if (result.status !== 200) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        if (!Array.isArray(data.items)) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_UNKNOWN}`, { code: 'E_RESPONSE_UNKNOWN', status: result.status });
        }
        return data;
    }
    /**
     * Creates both the meta and the media for a file.
     *
     * @param meta The file meta
     * @param contents The file contents
     * @param opts Meta and media create options.
     * @param request Optional request details
     * @returns The id of the created file meta.
     */
    async create(meta, contents, organization, opts = {}, request = {}) {
        const result = await this.createMeta(meta, organization, opts, request);
        await this.createMedia(contents, organization, result.key, opts, request);
        return result;
    }
    /**
     * Creates a file in the store.
     *
     * @param file The definition of a file that extends the IFile interface.
     * @param opts Optional options when creating a file
     * @param request Optional request options.
     * @returns The key of the creates file.
     */
    async createMeta(file, organization, opts = {}, request = {}) {
        const { token } = request;
        const url = this.sdk.getUrl(RouteBuilder.files(organization));
        if (opts.parent) {
            url.searchParams.set('parent', opts.parent);
        }
        const body = JSON.stringify(file);
        const result = await this.sdk.http.post(url.toString(), {
            token,
            body,
            headers: {
                'content-type': 'application/json',
            },
        });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to create a file. ';
        if (result.status !== 201) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        if (!data.key) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_UNKNOWN}`, { code: 'E_RESPONSE_UNKNOWN', status: result.status });
        }
        return data;
    }
    /**
     * Creates a file contents (the media) after the file meta was created.
     *
     * @param contents The file contents to upload to the server.
     * @param key The created file meta key.
     * @param opts Contents create options. You can define content's mime type here.
     * @param request Optional request.
     */
    async createMedia(contents, organization, key, opts = {}, request = {}) {
        const { token } = request;
        const path = RouteBuilder.fileMedia(organization, key);
        const url = this.sdk.getUrl(path);
        const mime = opts.mime || 'application/json';
        let body;
        if (mime.includes('json')) {
            body = JSON.stringify(contents);
        }
        else {
            body = String(contents);
        }
        const result = await this.sdk.http.put(url.toString(), {
            token,
            body,
            headers: {
                'content-type': mime,
            },
        });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to create a file. ';
        if (result.status !== 200) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
    }
    async createFolder(name, organization, opts = {}, request = {}) {
        const file = Folder.fromName(name);
        const { token } = request;
        const url = this.sdk.getUrl(RouteBuilder.files(organization));
        if (opts.parent) {
            url.searchParams.set('parent', opts.parent);
        }
        const body = JSON.stringify(file);
        const result = await this.sdk.http.post(url.toString(), {
            token,
            body,
            headers: {
                'content-type': 'application/json',
            },
        });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to create a file. ';
        if (result.status !== 201) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        if (!data.key) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_UNKNOWN}`, { code: 'E_RESPONSE_UNKNOWN', status: result.status });
        }
        return data;
    }
    /**
     * Reads a file from the store.
     *
     * @param oid Organization ID.
     * @param fid The file key
     * @param request Optional request options.
     */
    async read(oid, fid, request = {}) {
        const { token } = request;
        const url = this.sdk.getUrl(RouteBuilder.file(oid, fid));
        const result = await this.sdk.http.get(url.toString(), { token });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to read a file. ';
        if (result.status !== 200) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        if (!data.kind) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_UNKNOWN}`, { code: 'E_RESPONSE_UNKNOWN', status: result.status });
        }
        return data;
    }
    async readMedia(oid, fid, request = {}) {
        const { token } = request;
        const url = this.sdk.getUrl(RouteBuilder.fileMedia(oid, fid));
        const result = await this.sdk.http.get(url.toString(), { token });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to read a file media. ';
        if (result.status !== 200) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        const versionStr = result.headers.get('x-version');
        if (!versionStr) {
            throw new Exception(`${E_PREFIX}The file media version was not sent by the server.`, {
                status: result.status,
                code: 'E_RESPONSE_NO_VERSION',
            });
        }
        const version = Number(versionStr);
        if (isNaN(version)) {
            throw new Exception(`${E_PREFIX}The file media version is not a number.`, {
                status: result.status,
                code: 'E_RESPONSE_INVALID_VERSION',
            });
        }
        return {
            media: data,
            version,
        };
    }
    /**
     * Reads a number of files in a bulk operation.
     *
     * @param keys The list of keys to read. When the user has no access to the file it returns undefined
     * in that place. It also inserts `undefined` in place of a file that doesn't exist.
     * @param request Optional request options.
     */
    async readBulk(organization, keys, request = {}) {
        const { token } = request;
        const url = this.sdk.getUrl(RouteBuilder.filesBatch(organization));
        const body = JSON.stringify({ ids: keys });
        const result = await this.sdk.http.post(url.toString(), { token, body });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to read files in bulk. ';
        if (result.status !== 200) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        if (!Array.isArray(data.items)) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_UNKNOWN}`, { code: 'E_RESPONSE_UNKNOWN', status: result.status });
        }
        return data;
    }
    /**
     * Patches a file in the store.
     * @param fid The key of the file to patch
     * @param info The JSON patch to be processed.
     * @param request Optional request options.
     * @returns The updated file object after applying the patch
     */
    async patch(oid, fid, info, request = {}) {
        const { token } = request;
        const path = RouteBuilder.file(oid, fid);
        const url = this.sdk.getUrl(path);
        const body = JSON.stringify(info);
        const result = await this.sdk.http.patch(url.toString(), { token, body });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to patch a file. ';
        if (result.status !== 200) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        if (!data.kind) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_UNKNOWN}`, { code: 'E_RESPONSE_UNKNOWN', status: result.status });
        }
        return data;
    }
    /**
     * Patches file media.
     * @param oid Organization id
     * @param fid File id
     * @param info THe patch to send
     * @param req Optional request
     * @returns The server response.
     */
    async patchMedia(oid, fid, info, req = {}) {
        const { token } = req;
        const path = RouteBuilder.fileMedia(oid, fid);
        const url = this.sdk.getUrl(path);
        const body = JSON.stringify(info);
        const result = await this.sdk.http.patch(url.toString(), { token, body });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to patch a file contents. ';
        if (result.status !== 200) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        return data;
    }
    /**
     * Deletes the file in the store.
     *
     * @param fid The key of the file to delete.
     * @param request Optional request options.
     * @returns The key of the created trash entry.
     */
    async delete(oid, fid, request = {}) {
        const { token } = request;
        const url = this.sdk.getUrl(RouteBuilder.file(oid, fid));
        const result = await this.sdk.http.delete(url.toString(), { token });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to trash a file. ';
        if (result.status !== 204) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
    }
    async deleteBulk(oid, fids, request = {}) {
        const { token } = request;
        const url = this.sdk.getUrl(RouteBuilder.files(oid));
        const result = await this.sdk.http.delete(url.toString(), { token, body: JSON.stringify({ ids: fids }) });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to trash files. ';
        if (result.status !== 204) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
    }
    /**
     * Updates the sharing options of the file.
     *
     * @param oid The parent organization key
     * @param fid The file key
     * @param info The patch operation on the file's ACL
     * @param request Optional request options.
     */
    async patchUsers(oid, fid, info, request = {}) {
        const { token } = request;
        const url = this.sdk.getUrl(RouteBuilder.filesAccess(oid, fid));
        const body = JSON.stringify(info);
        const result = await this.sdk.http.patch(url.toString(), { token, body });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to patch the file. ';
        if (result.status !== 200) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        if (!data.key) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_UNKNOWN}`, { code: 'E_RESPONSE_UNKNOWN', status: result.status });
        }
        return data;
    }
    /**
     * A shorthand method to add access to the file to a single user.
     *
     * @param organization The parent organization key
     * @param key The file key
     * @param otherUserKey The key of the user that gets access to the organization
     * @param role The user role to grant
     * @param expires Optionally, when the permission expires.
     */
    async addUser(organization, key, otherUserKey, role, expires, request) {
        const op = {
            op: 'add',
            type: 'user',
            value: role,
            id: otherUserKey,
        };
        if (typeof expires === 'number') {
            op.expirationTime = expires;
        }
        const patch = {
            patch: [op],
            id: nanoid(),
        };
        return this.patchUsers(organization, key, patch, request);
    }
    /**
     * A shorthand method to remove access to the organization to a single user.
     *
     * @param organization The organization key to share
     * @param key The file key
     * @param otherUserKey The key of the user that gets access to the organization
     */
    async removeUser(organization, key, otherUserKey, request) {
        const op = {
            op: 'remove',
            type: 'user',
            id: otherUserKey,
        };
        const patch = {
            patch: [op],
            id: nanoid(),
        };
        return this.patchUsers(organization, key, patch, request);
    }
    /**
     * Lists uses having access to the file.
     *
     * @param key The file key
     * @param request Optional request options.
     */
    async listUsers(organization, key, request = {}) {
        const { token } = request;
        const url = this.sdk.getUrl(RouteBuilder.fileUsers(organization, key));
        const result = await this.sdk.http.get(url.toString(), { token });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to list users in the file. ';
        if (result.status !== 200) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        if (!Array.isArray(data.items)) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_UNKNOWN}`, { code: 'E_RESPONSE_UNKNOWN', status: result.status });
        }
        return data;
    }
    /**
     * Lists breadcrumbs to the file.
     * @param key The lowest file in the hierarchy
     */
    async breadcrumbs(organization, key, request = {}) {
        const { token } = request;
        const url = this.sdk.getUrl(RouteBuilder.fileBreadcrumbs(organization, key));
        const result = await this.sdk.http.get(url.toString(), { token });
        this.inspectCommonStatusCodes(result);
        const E_PREFIX = 'Unable to read breadcrumbs. ';
        if (result.status !== 200) {
            this.logInvalidResponse(result);
            throw this.createApiError(`${E_PREFIX}${E_RESPONSE_STATUS}${result.status}`, result.body);
        }
        if (!result.body) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_NO_VALUE}`, { code: 'E_RESPONSE_NO_VALUE', status: result.status });
        }
        let data;
        try {
            data = JSON.parse(result.body);
        }
        catch {
            throw new Exception(`${E_PREFIX}${E_INVALID_JSON}`, { code: 'E_INVALID_JSON', status: result.status });
        }
        if (!Array.isArray(data.items)) {
            throw new Exception(`${E_PREFIX}${E_RESPONSE_UNKNOWN}`, { code: 'E_RESPONSE_UNKNOWN', status: result.status });
        }
        return data;
    }
}
//# sourceMappingURL=FilesSdk.js.map