import * as i0 from '@angular/core';
import { Injectable } from '@angular/core';
import * as i2 from '@c8y/client';
import { ApplicationType, ApplicationAvailability } from '@c8y/client';
import * as i1 from '@c8y/ngx-components';

class StaticAssetsService {
    constructor(appstate, appService, fetchClient, fileService, zip) {
        this.appstate = appstate;
        this.appService = appService;
        this.fetchClient = fetchClient;
        this.fileService = fileService;
        this.zip = zip;
        this.fileNames = {
            contents: 'contents.json',
            manifest: 'cumulocity.json',
            exportZipName: 'static-assets.zip'
        };
        this.listFilesCachePromises = {};
    }
    /**
     * Lists all files of the given type.
     * Forces a cache renewal.
     */
    async listFiles(type) {
        return this.listFilesCached(type, true);
    }
    /**
     * Lists all files of the given type.
     * Uses a cache.
     */
    async listFilesCached(type, forceCacheRenewal = false) {
        if (!this.listFilesCachePromises[type] || forceCacheRenewal) {
            this.listFilesCachePromises[type] = this._listFiles(type);
        }
        return await this.listFilesCachePromises[type];
    }
    /**
     * Clones all assets of the given type from parent tenants into the current tenant.
     */
    async cloneAssetsIntoTenant(type) {
        const { data: apps } = await this.appService.list({
            availability: ApplicationAvailability.SHARED,
            type: ApplicationType.HOSTED,
            pageSize: 2000
        });
        const tenantIdsFromOtherAssetApps = apps
            .filter(app => {
            const result = app.owner?.tenant?.id &&
                app.owner?.tenant?.id !== this.appstate.currentTenant.value?.name &&
                app.contextPath === this.getContextPath(type, app.owner?.tenant?.id);
            return result;
        })
            .map(app => app.owner?.tenant?.id);
        let filesOfCurrentTenant = await this._listFiles(type);
        let filesToUpload = new Array();
        const oldAssets = new Array();
        for (const tenantId of tenantIdsFromOtherAssetApps) {
            try {
                const filesOfApp = await this.getContentJSONForType(type, tenantId);
                oldAssets.push(...filesOfApp);
                for (const file of filesOfApp) {
                    try {
                        const response = await this.fetchClient.fetch(file.path);
                        if (response.status !== 200) {
                            console.warn(`Failed to get file "${file.fileName}" from path ${file.path}`);
                            continue;
                        }
                        const fileContent = await response.blob();
                        // remove existing file with same name
                        filesOfCurrentTenant = filesOfCurrentTenant.filter(existingFile => existingFile.fileName !== file.fileName);
                        filesToUpload = filesToUpload.filter(existingFile => existingFile.name !== file.fileName);
                        filesToUpload.push(new File([fileContent], file.fileName, { type: file.type }));
                    }
                    catch (e) {
                        console.warn(`Failed to add file "${file.fileName}" from tenant ${tenantId}`, e);
                    }
                }
            }
            catch (e) {
                console.warn(`Failed to get asset files from tenant ${tenantId}`);
            }
        }
        if (!filesToUpload.length) {
            return;
        }
        const newAssets = await this.addFilesToStaticAssets(type, filesToUpload, filesOfCurrentTenant);
        return { oldAssets, newAssets };
    }
    /**
     * Adds the given files to the static assets of the given type.
     */
    async addFilesToStaticAssets(type, files, existingFiles, throwErrorOnExistingFiles = true) {
        const app = await this.getAppForTenant(type);
        const filesToAddToContents = new Array();
        const addedAt = new Date().toISOString();
        const filesToUpload = [];
        for (const droppedFile of files) {
            const file = droppedFile instanceof File ? droppedFile : droppedFile.file;
            const fileName = file.name.toLowerCase().replace(/[^a-zA-Z0-9\-\_\.]/g, '');
            const newFile = new File([file], fileName, {
                type: file.type,
                lastModified: file.lastModified
            });
            if (existingFiles.find(existingFile => existingFile.fileName === fileName)) {
                if (throwErrorOnExistingFiles) {
                    throw Error(`File with name "${fileName}" is already existing.`);
                }
                existingFiles = existingFiles.filter(existingFile => existingFile.fileName !== fileName);
            }
            const fileExtension = file.name.split('.').pop();
            filesToAddToContents.push({
                addedAt,
                lastModified: file.lastModified,
                extension: fileExtension,
                fileName: fileName,
                originalFileName: file.name,
                path: `/apps/public/${app.contextPath}/${fileName}`,
                size: newFile.size,
                type: newFile.type,
                hashSum: await this.fileService.getHashSumOfFile(newFile)
            });
            filesToUpload.push({
                contents: newFile,
                path: fileName
            });
        }
        const newFiles = [...existingFiles, ...filesToAddToContents];
        filesToUpload.push({
            contents: new File([JSON.stringify(newFiles)], this.fileNames.contents, {
                type: 'application/json'
            }),
            path: this.fileNames.contents
        });
        await this.appService.binary(app).updateFiles(filesToUpload);
        await this.ensureAddedFilesArePresent(filesToAddToContents);
        return newFiles;
    }
    /**
     * Gets the static assets app for the given type.
     */
    async getAppForTenant(type) {
        const contextPath = this.getContextPath(type);
        const { data: manifest } = await this.appService.getManifestOfContextPath(contextPath);
        if (!manifest.id) {
            throw Error(`No app with path ${contextPath} found.`);
        }
        const { data: app } = await this.appService.detail(manifest.id);
        if (app.owner?.tenant.id !== this.appstate.currentTenant.value?.name) {
            throw Error(`No app with path ${contextPath} found that is owned by the current tenant.`);
        }
        return app;
    }
    getContentJSONForType(type, appendTenantId) {
        const path = this.getPathForContent(type, appendTenantId);
        return this.getContentJSONFromPath(path);
    }
    async getContentJSONFromPath(path) {
        const response = await this.fetchClient.fetch(path);
        if (response.status !== 200) {
            throw Error(`Failed to retrieve ${path}`);
        }
        const content = await response.json();
        return content;
    }
    async ensureAddedFilesArePresent(files) {
        for (const file of files) {
            await this.ensureFileIsPresent(file);
        }
    }
    async ensureFileIsPresent(file) {
        for (let i = 0; i < 12; i++) {
            try {
                const currentDate = new Date();
                const response = await this.fetchClient.fetch(`${file.path}?nocache=${currentDate.getTime()}`);
                if (response.status !== 200) {
                    await this.sleep(5_000);
                    continue;
                }
                const blob = await response.blob();
                const hashSum = await this.fileService.getHashSumOfFile(blob);
                if (hashSum !== file.hashSum) {
                    await this.sleep(5_000);
                    continue;
                }
                return;
            }
            catch (e) {
                console.warn(e);
                continue;
            }
        }
        throw Error(`Unable to retrieve file from ${file.path}`);
    }
    sleep(duration) {
        return new Promise(resolve => setTimeout(() => resolve(), duration));
    }
    async _listFiles(type) {
        try {
            const files = await this.getContentJSONForType(type);
            return files;
        }
        catch (e) {
            console.warn(e);
        }
        return await this.createEmptyApp(type);
    }
    getPathForContent(type, appendTenantId) {
        const currentDate = new Date();
        const contextPath = this.getContextPath(type, appendTenantId);
        return `/apps/public/${contextPath}/${this.fileNames.contents}?nocache=${currentDate.getTime()}`;
    }
    getContextPath(type, appendTenantId) {
        let contextPath = type + '-assets';
        const tenantId = appendTenantId === undefined ? this.appstate.currentTenant.value?.name : appendTenantId;
        if (tenantId) {
            contextPath = `${contextPath}-${tenantId}`;
        }
        return contextPath;
    }
    async createEmptyApp(type, appendTenantId) {
        const name = this.getContextPath(type, false);
        const contextPath = this.getContextPath(type, appendTenantId);
        const key = `${contextPath}-key`;
        const manifest = {
            name,
            contextPath,
            key,
            description: `Application containing static assets used for ${type}.`,
            noAppSwitcher: true,
            type: ApplicationType.HOSTED,
            availability: ApplicationAvailability.MARKET
        };
        const content = [];
        const zipFile = await this.zip.createZip([
            {
                fileName: `${this.fileNames.manifest}`,
                blob: new Blob([JSON.stringify(manifest)])
            },
            {
                fileName: `${this.fileNames.contents}`,
                blob: new Blob([JSON.stringify(content)])
            }
        ]);
        const { data: app } = await this.appService.create(manifest);
        const { data: appBinary } = await this.appService
            .binary(app)
            .upload(zipFile, `empty-${name}.zip`);
        await this.appService.update({ id: app.id, activeVersionId: appBinary.id });
        return [];
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: StaticAssetsService, deps: [{ token: i1.AppStateService }, { token: i2.ApplicationService }, { token: i2.FetchClient }, { token: i1.FilesService }, { token: i1.ZipService }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: StaticAssetsService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: StaticAssetsService, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }], ctorParameters: () => [{ type: i1.AppStateService }, { type: i2.ApplicationService }, { type: i2.FetchClient }, { type: i1.FilesService }, { type: i1.ZipService }] });

/**
 * Generated bundle index. Do not edit.
 */

export { StaticAssetsService };
//# sourceMappingURL=c8y-ngx-components-static-assets-data.mjs.map
