import { Discipline } from '../discipline';
import { BimIfcClass } from '../bim-ifc-class';
import { BimChangeType, Lcid } from '../bim-api-client';
import { HttpResponseType, createMockTypedResponse } from '../../http';
import { OwnerSystem } from '../OwnerSystem';
import { BimUserSettingsApiClient } from './BimUserSettingsApiClient';
import { LayerDefinitions } from '../LayerDefinitions';
import { Permissions } from '../Permission';
import { IfcSiteInformationImplementation } from '../IfcSiteInformation';
import { PredefinedBimChangeMetadataQuery } from '../PredefinedBimChangeMetadataQuery';
/**
 * Loads container and IFC data from local files. Mostly used for dev purposes.
 * @hidden
 */
export class BimDevApiClient {
    containersAndFiles;
    _rootDummyProjectId = 'root';
    _anchorElement = document.createElement('a');
    layers;
    messages;
    upload;
    settings;
    mapBox;
    twins;
    id = 'dev-api-client';
    _mockUser = {
        id: '1',
        name: 'Pelle Fake',
        firstName: 'Pelle',
        lastName: 'Fake',
        privacyPolicyUrl: 'http://foo.com/privacyPolicy',
        email: 'pellefake@faker.com'
    };
    constructor(containersAndFiles) {
        this.containersAndFiles = containersAndFiles;
        const notImplemented = () => {
            throw new Error('Not implemented');
        };
        // TODO: Create own dev class when we actually need to have implemented
        // methods
        this.layers = {
            update: notImplemented,
            add: notImplemented
        };
        this.messages = {
            sendMailMessage: notImplemented
        };
        this.upload = {
            createFileSession: notImplemented,
            canAppendFileVersion: notImplemented,
            canCreateFileInFolder: notImplemented,
            canCreateFileIn: notImplemented
        };
        this.settings = {
            user: new BimUserSettingsApiClient('')
        };
        this.mapBox = {
            getAccessToken: notImplemented
        };
        this.twins = {
            listTwins: notImplemented,
            listAllTwins: notImplemented,
            getTwin: notImplemented,
            listTwinVersions: notImplemented,
            downloadTwinContent: notImplemented,
            loadTwin: notImplemented,
            loadTwinWithFactories: notImplemented,
            loadGeometryInfo: notImplemented,
            loadGeometryInfoWithFactories: notImplemented,
            loadTwinWithGeometry: notImplemented,
            getTwinDiagnostics: notImplemented,
            getTwinVersionDiagnostics: notImplemented,
            createOrUpdateTwin: notImplemented,
            updateTwin: notImplemented,
            restoreTwin: notImplemented,
            getTwinVersionContent: notImplemented,
            getTwinContent: notImplemented,
            getTwinVersionEvents: notImplemented,
            getProcessorEvents: notImplemented,
            listAssets: notImplemented,
            getAsset: notImplemented,
            createAssets: notImplemented,
            updateAsset: notImplemented,
            deleteAsset: notImplemented,
            restoreAsset: notImplemented,
            batchAssets: notImplemented,
            batchGetAssets: notImplemented,
            listTwinFiles: notImplemented,
            getTwinFile: notImplemented,
            createOrUpdateTwinFile: notImplemented,
            deleteTwinFile: notImplemented,
            restoreTwinFile: notImplemented,
            getTwinFileContent: notImplemented,
            getTwinFileVersion: notImplemented,
            listTwinFileVersions: notImplemented,
            getTwinFileVersionContent: notImplemented,
            deleteTwin: notImplemented
        };
    }
    canDelete(change) {
        return false;
    }
    deleteChanges(changesToDelete) {
        throw new Error('not implemented');
    }
    getUserInfo() {
        return Promise.resolve(createMockTypedResponse({ value: this._mockUser }));
    }
    /** See {@link TwinfinityApiClient.getContainerInfo} */
    getContainerInfo(containerOrUrl) {
        let containerId = this._rootDummyProjectId;
        if (containerOrUrl) {
            if ('url' in containerOrUrl) {
                containerOrUrl = new URL(containerOrUrl.url);
            }
            containerId = this.getContainerId(containerOrUrl);
            if (!containerId) {
                throw new Error(`${containerOrUrl.href} does not exist.`);
            }
        }
        return Promise.resolve(createMockTypedResponse({
            value: {
                id: containerId,
                title: containerId,
                language: 'en-US',
                info: {
                    title: containerId,
                    language: 'en-US'
                },
                user: this._mockUser
            }
        }));
    }
    /** See {@link TwinfinityApiClient.getContainers} */
    async getContainers(id) {
        let ret = Object.keys(this.containersAndFiles).map((id) => {
            const serverRelativeUrl = '?container=' + id;
            const url = this.makeAbsoluteUrl(serverRelativeUrl).toString();
            return {
                type: BimChangeType.Container,
                ownerSystem: OwnerSystem.twinfinity.id,
                containerId: '-',
                url: this.makeAbsoluteUrl(serverRelativeUrl).toString(),
                path: this.makeAbsoluteUrl(serverRelativeUrl).pathname,
                id: id,
                format: 'web',
                name: id,
                version: 0,
                metadata: {},
                language: 'en-US',
                lcid: Lcid.Swedish,
                modifiedUtc: new Date(),
                hasGeometry: true,
                serverRelativeUrl: serverRelativeUrl,
                apiUrl: url.toString(),
                containerType: 'ps',
                etag: '123',
                permissionsString: Permissions.join('view').toString(),
                permissions: new Permissions('view'),
                availableLayerDefinitions: new LayerDefinitions()
            };
        });
        if (id !== undefined) {
            ret = ret.filter((c) => c.id === id);
        }
        return Promise.resolve(ret);
    }
    // TODO Add some kind of filter here so users can decide what type of files to get
    // perhaps filtered on metadata or similar. Also allows us to get specific files etc etc
    // graphql might be a good fit for these kind of things. If we do that we might not need
    // functions for different types of changes (diagnostics etc).
    // Initially we can use some kind of jsonb to perform the search?
    /** See {@link TwinfinityApiClient.getIfcChanges} */
    async getIfcChanges(containerOrUrl, olderThan) {
        return (await this.getChanges(containerOrUrl, PredefinedBimChangeMetadataQuery.ifc())).value;
    }
    async getChanges(containerOrUrl, options) {
        if ('url' in containerOrUrl) {
            if (containerOrUrl.containerId === this._rootDummyProjectId) {
                return createMockTypedResponse({ value: [] });
            }
            containerOrUrl = new URL(containerOrUrl.url);
        }
        const containerId = this.getContainerId(containerOrUrl);
        if (!containerId) {
            throw new Error('Missing ?container=<name> on query string');
        }
        const fileNames = this.containersAndFiles[containerId];
        const ret = [];
        const shouldLoadIfc = !('query' in options) || options.query === '' || options.query.startsWith('ifc');
        const shouldLoadDwg = !('query' in options) || options.query === '' || options.query.startsWith('dwg');
        for (const fn of fileNames) {
            const apiUrl = this.makeAbsoluteUrl(`dist/${containerId}/`);
            const fakeUrl = new URL(`${fn}?_r=${fn}`.replace(/^\/+/, ''), apiUrl);
            const metadataResponse = await this.get(fakeUrl.origin + fakeUrl.pathname + '.metadata' + fakeUrl.search + fakeUrl.hash, HttpResponseType.json);
            if (shouldLoadIfc && fn.endsWith('.ifc')) {
                const metadata = metadataResponse.status === 200 ? await metadataResponse.value : {};
                metadata.sites = (metadata.sites ?? []).map((site) => new IfcSiteInformationImplementation(site));
                const ifcChange = {
                    type: BimChangeType.Ifc,
                    ownerSystem: OwnerSystem.twinfinity.id,
                    url: fakeUrl.toString(),
                    path: fakeUrl.pathname,
                    containerId: containerId,
                    blobId: 'blob_' + fn,
                    id: fn,
                    format: 'ifc',
                    discipline: Discipline.getOrAddFromServerRelativeUrl(fakeUrl.pathname),
                    productCount: metadata.statistics?.productCount ?? -1,
                    name: fn,
                    version: 0,
                    etag: '123',
                    metadata: { ifc: metadata, serverRelativeUrl: apiUrl.pathname },
                    floors: metadata.floors ?? [],
                    classes: (metadata.classes ?? []).map((c) => BimIfcClass.getOrAdd(c)),
                    permissionsString: Permissions.join('view', 'add').toString(),
                    permissions: new Permissions('view', 'add'),
                    apiUrl: apiUrl.toString(),
                    resourceUrl: {
                        idx: new URL(fakeUrl.origin + fakeUrl.pathname + '.idx' + fakeUrl.search + fakeUrl.hash),
                        geom: new URL(fakeUrl.origin + fakeUrl.pathname + '.geom' + fakeUrl.search + fakeUrl.hash),
                        t8cm: new URL(fakeUrl.origin + fakeUrl.pathname + '.t8cm' + fakeUrl.search + fakeUrl.hash),
                        t8gi: new URL(fakeUrl.origin + fakeUrl.pathname + '.t8gi' + fakeUrl.search + fakeUrl.hash),
                        t8gd: new URL(fakeUrl.origin + fakeUrl.pathname + '.t8gd' + fakeUrl.search + fakeUrl.hash),
                        'geom-feat': new URL(fakeUrl.origin + fakeUrl.pathname + '.geom-feat' + fakeUrl.search + fakeUrl.hash),
                        prop: new URL(fakeUrl.origin + fakeUrl.pathname + '.prop' + fakeUrl.search + fakeUrl.hash)
                    },
                    availableLayerDefinitions: new LayerDefinitions()
                };
                if (!(await fetch(ifcChange.resourceUrl.t8cm, { method: 'HEAD' })).ok) {
                    ifcChange.resourceUrl.t8cm = undefined;
                }
                if (!(await fetch(ifcChange.resourceUrl.t8gi, { method: 'HEAD' })).ok) {
                    ifcChange.resourceUrl.t8gi = undefined;
                }
                if (!(await fetch(ifcChange.resourceUrl.t8gd, { method: 'HEAD' })).ok) {
                    ifcChange.resourceUrl.t8gd = undefined;
                }
                ret.push(ifcChange);
            }
            else if (shouldLoadDwg && fn.endsWith('.dwg')) {
                const metadata = metadataResponse.status === 200 ? await metadataResponse.value : {};
                metadata.url = { svg: fakeUrl.origin + fakeUrl.pathname + '.svg' + fakeUrl.search + fakeUrl.hash };
                const ifcChange = {
                    type: BimChangeType.Dwg,
                    ownerSystem: OwnerSystem.twinfinity.id,
                    url: fakeUrl.toString(),
                    path: fakeUrl.pathname,
                    containerId: containerId,
                    blobId: 'blob_' + fn,
                    id: fn,
                    format: 'ifc',
                    discipline: Discipline.getOrAddFromServerRelativeUrl(fakeUrl.pathname),
                    name: fn,
                    version: 0,
                    etag: '123',
                    metadata: { dwg: metadata, serverRelativeUrl: apiUrl.pathname },
                    permissionsString: Permissions.join('view', 'add').toString(),
                    permissions: new Permissions('view', 'add'),
                    apiUrl: apiUrl.toString(),
                    availableLayerDefinitions: new LayerDefinitions()
                };
                ret.push(ifcChange);
            }
        }
        return createMockTypedResponse({ value: ret });
    }
    /** See {@link TwinfinityApiClient.get} */
    async get(absoluteOrRelativeUrl, converter, init) {
        const response = await fetch(absoluteOrRelativeUrl.toString());
        return converter(response);
    }
    /** See {@link TwinfinityApiClient.getInfo} */
    async getInfo() {
        return {
            name: 'Dev',
            logoImageUrl: 'http://foo.com/twinfinity/bar.png',
            backgroundImageUrl: 'http://foo.com/twinfinity/bar.png',
            psUrl: 'http://foo.com',
            version: '0.0.0.0'
        };
    }
    makeAbsoluteUrl(url) {
        this._anchorElement.href = url.toString();
        return new URL(this._anchorElement.href);
    }
    getContainerId(url) {
        return url.searchParams.get('container') ?? 'root';
    }
}
//# sourceMappingURL=BimDevApiClient.js.map