/** @internal NOTE: Internal APIs. Subject to change. Use of these APIs in production applications is not supported. */
import { Lookup } from '../lookup';

import { BimChangeLayer, BimChange, BimChangeLayerData, BlobData } from '../bim-api-client';
import { TypedResponse, HttpResponseType, HttpStatusCode, Http } from '../../http';
import { toBlob } from '../../blob';
import { LayerApiClient } from './LayerApiClient';
import { BimCoreApiClient } from './BimCoreApiClient';
import { clone } from '../../util';

/** See {@link LayerApi} for documentation.  */
export class BimLayerApiClient implements LayerApiClient {
    private static readonly _httpResponseType = HttpResponseType.status([HttpStatusCode.Ok, HttpStatusCode.Conflict]);

    public constructor(readonly _apiClient: BimCoreApiClient) {}

    public async add(
        attachTo: Pick<BimChange, 'id' | 'apiUrl'> & Partial<Pick<BimChange, 'name'>>,
        data: BimChangeLayerData
    ): Promise<TypedResponse<BimChangeLayer>> {
        const form = new FormData();

        form.append(
            'layer',
            JSON.stringify({
                attachToId: attachTo.id,
                format: data.format,
                metadata: this.trimReservedMetadata(data.metadata)
            })
        );

        if (data.data) {
            form.append('data', toBlob(data.data));
        }

        return await Http.post<BimChangeLayer>(
            `${attachTo.apiUrl}file/layer`,
            BimLayerApiClient._httpResponseType.json,
            { body: form }
        );
    }

    public async update(
        layer: Pick<BimChangeLayer, 'etag' | 'id' | 'metadata' | 'name' | 'apiUrl'>,
        options?: {
            data?: BlobData;
            overwrite?: boolean;
        }
    ): Promise<TypedResponse<BimChangeLayer>> {
        const headers = new Headers({
            overwrite: (options?.overwrite ?? false).toString()
        });
        const form = new FormData();
        form.append(
            'layer',
            JSON.stringify({
                id: layer.id,
                etag: layer.etag,
                name: layer.name,
                metadata: this.trimReservedMetadata(layer.metadata)
            })
        );

        if (options?.data) {
            form.append('data', toBlob(options.data));
        }
        return await Http.put<BimChangeLayer>(`${layer.apiUrl}file/layer`, BimLayerApiClient._httpResponseType.json, {
            body: form,
            headers
        });
    }

    /** Removes metadata that can only be set server side to avoid sever side validation errors */
    private trimReservedMetadata(metadata?: Lookup<any>): Lookup<any> | undefined {
        if (metadata?._system) {
            const deepCopyMetadata = clone(metadata) as Lookup<any>;
            delete deepCopyMetadata._system;
            return deepCopyMetadata;
        } else if (metadata) {
            return metadata;
        }
    }
}
