import * as i0 from '@angular/core';
import { Injectable, Inject, InjectionToken, Optional } from '@angular/core';
import { firstValueFrom, ReplaySubject } from 'rxjs';
import * as i1 from '@angular/common/http';
import { HttpClient } from '@angular/common/http';
import { finalize, share } from 'rxjs/operators';
import { SetToObject, getFromObject, deepMerge, coerceArray, SetObjectValue } from '@rxap/utilities';

class ConfigLoaderService {
    constructor(http) {
        this.http = http;
        this.configs = new Map();
        this.configLoading = new Map();
    }
    async load$(url) {
        if (this.configs.has(url)) {
            return this.configs.get(url);
        }
        if (this.configLoading.has(url)) {
            return this.configLoading.get(url).toPromise();
        }
        const loading$ = this.http.get(url).pipe(finalize(() => this.configLoading.delete(url)), share());
        this.configLoading.set(url, loading$);
        return firstValueFrom(loading$);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: ConfigLoaderService, deps: [{ token: HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: ConfigLoaderService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: ConfigLoaderService, decorators: [{
            type: Injectable,
            args: [{ providedIn: 'root' }]
        }], ctorParameters: () => [{ type: i1.HttpClient, decorators: [{
                    type: Inject,
                    args: [HttpClient]
                }] }] });

class ConfigTestingService {
    constructor() {
        this.config = {};
    }
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    clearLocalConfig() {
    }
    set(propertyPath, value) {
        SetToObject(this.config, propertyPath, value);
    }
    get(propertyPath, defaultValue) {
        return getFromObject(this.config, propertyPath, defaultValue);
    }
    getOrThrow(propertyPath) {
        const value = this.get(propertyPath);
        if (value === undefined) {
            throw new Error(`Could not find config in path '${propertyPath}'`);
        }
        return value;
    }
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    setLocalConfig(config) {
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: ConfigTestingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: ConfigTestingService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: ConfigTestingService, decorators: [{
            type: Injectable
        }] });

const RXAP_CONFIG = new InjectionToken('rxap/config');

class ConfigService {
    static { this.onError = new ReplaySubject(1); }
    static { this.onRequestError = new ReplaySubject(1); }
    static { this.onErrorFnc = []; }
    static { this.onRequestErrorFnc = []; }
    static { this.Config = null; }
    /**
     * Static default values for the config object.
     * Will be overwritten by an dynamic config file specified in
     * the Urls array.
     */
    static { this.Defaults = {}; }
    /**
     * Any value definition in the Overwrites object will overwrite any
     * value form the Defaults values or dynamic config files
     */
    static { this.Overwrites = {}; }
    static { this.LocalStorageKey = 'rxap/config/local-config'; }
    /**
     * @deprecated instead use the url property of the ConfigLoadOptions
     */
    static { this.Urls = []; }
    constructor(config = null) {
        this.config = ConfigService.Config;
        if (config) {
            this.config = deepMerge(this.config ?? {}, config);
        }
        if (!this.config) {
            throw new Error('config not available');
        }
    }
    /**
     * Used to load the app config from a remote resource.
     *
     * Promise.all([ ConfigService.Load() ])
     * .then(() => platformBrowserDynamic().bootstrapModule(AppModule))
     * .catch(err => console.error(err))
     *
     */
    static async Load(options, environment) {
        options ??= {};
        let config = deepMerge(this.Defaults, {});
        if (environment?.config) {
            if (typeof environment.config === 'string') {
                options.url = environment.config;
            }
            else {
                options.static = environment.config.static;
                options.url = environment.config.url;
                options.fromUrlParam = environment.config.fromUrlParam;
                options.fromLocalStorage = environment.config.fromLocalStorage;
                options.schema = environment.config.schema;
            }
        }
        config = deepMerge(config, options?.static ?? {});
        const urls = (options?.url ? coerceArray(options.url) : ConfigService.Urls).map(url => {
            if (typeof url === 'function') {
                if (!environment) {
                    throw new Error('environment is required when url is a function');
                }
                return coerceArray(url(environment));
            }
            return coerceArray(url);
        }).flat();
        for (const url of urls) {
            config = deepMerge(config, await this.loadConfig(url, true, options?.schema));
        }
        config = deepMerge(config, this.Overwrites);
        if (options?.fromLocalStorage !== false) {
            const localConfig = localStorage.getItem(ConfigService.LocalStorageKey);
            if (localConfig) {
                try {
                    config = deepMerge(config, JSON.parse(localConfig));
                }
                catch (e) {
                    console.error('local config could not be parsed');
                }
            }
        }
        if (options?.fromUrlParam) {
            const param = typeof options.fromUrlParam === 'string' ? options.fromUrlParam : 'config';
            config = deepMerge(config, this.LoadConfigDefaultFromUrlParam(param));
        }
        console.debug('app config', config);
        this.Config = config;
    }
    static handleError(error) {
        this.onError.next(error);
        for (const fnc of this.onErrorFnc) {
            try {
                fnc(error);
            }
            catch (e) {
                console.error('Error in onErrorFnc', e);
            }
        }
    }
    static handleRequestError(response) {
        this.onRequestError.next(response);
        for (const fnc of this.onRequestErrorFnc) {
            try {
                fnc(response);
            }
            catch (e) {
                console.error('Error in onRequestErrorFnc', e);
            }
        }
    }
    static async loadConfig(url, required, schema) {
        let config;
        let response;
        try {
            response = await fetch(url);
        }
        catch (error) {
            const message = `Could not fetch config from '${url}': ${error.message}`;
            if (required) {
                this.handleError(error);
                this.showError(message);
                throw new Error(message);
            }
            else {
                console.warn(message);
                return null;
            }
        }
        if (!response.ok) {
            let message = `Config request results in non ok response for '${url}': (${response.status}) ${response.statusText}`;
            switch (response.status) {
                case 404:
                    message = `Config not found at '${url}'`;
                    break;
                case 405:
                    message = `Config service is not started yet. Wait 30s and try again.`;
                    break;
                case 401:
                    message = `Unauthorized to fetch config from '${url}'`;
                    break;
            }
            if (required) {
                this.handleRequestError(response);
                this.showError(message);
                throw new Error(message);
            }
            else {
                console.warn(message);
                return null;
            }
        }
        try {
            config = await response.json();
        }
        catch (error) {
            const message = `Could not parse config from '${url}' to a json object: ${error.message}`;
            if (required) {
                this.handleError(error);
                this.showError(message);
                throw new Error(message);
            }
            else {
                console.warn(message);
                return null;
            }
        }
        if (schema) {
            try {
                config = await schema.validateAsync(config);
            }
            catch (error) {
                const message = `Config from '${url}' is not valid: ${error.message}`;
                if (required) {
                    this.handleError(error);
                    this.showError(message);
                    throw new Error(message);
                }
                else {
                    console.warn(message);
                    return null;
                }
            }
        }
        return config;
    }
    static LoadConfigDefaultFromUrlParam(param = 'config') {
        const queryString = window.location.search;
        const urlParams = new URLSearchParams(queryString);
        const configFromParams = {};
        for (const configParam of urlParams.getAll('config')) {
            try {
                const split = configParam.split(';');
                if (split.length === 2) {
                    const keyPath = split[0];
                    const value = split[1];
                    SetObjectValue(configFromParams, keyPath, value);
                }
            }
            catch (e) {
                console.warn(`Parsing of url config param failed for '${configParam}': ${e.message}`);
            }
        }
        return configFromParams;
    }
    static async SideLoad(url, propertyPath, required, schema) {
        if (!this.Config) {
            throw new Error('Config side load is only possible after the initial config load.');
        }
        const config = await this.loadConfig(url, required, schema);
        SetObjectValue(this.Config, propertyPath, config);
        console.debug(`Side loaded config for '${propertyPath}' successful`, this.Config);
    }
    static Get(path, defaultValue, config = this.Config) {
        if (!config) {
            throw new Error('config not loaded');
        }
        let configValue = config;
        if (typeof path !== 'string') {
            throw new Error('The config property path is not a string');
        }
        for (const fragment of path.split('.')) {
            // eslint-disable-next-line no-prototype-builtins
            if (configValue.hasOwnProperty(fragment)) {
                configValue = configValue[fragment];
            }
            else {
                if (defaultValue !== undefined) {
                    return defaultValue;
                }
                console.warn(`Config with path '${path}' not found`);
                return undefined;
            }
        }
        return configValue;
    }
    static showError(message) {
        const hasUl = document.getElementById('rxap-config-error') !== null;
        const ul = document.getElementById('rxap-config-error') ?? document.createElement('ul');
        ul.id = 'rxap-config-error';
        ul.style.position = 'fixed';
        ul.style.bottom = '16px';
        ul.style.right = '16px';
        ul.style.backgroundColor = 'white';
        ul.style.padding = '32px';
        ul.style.zIndex = '99999999';
        ul.style.color = 'black';
        const messageLi = document.createElement('li');
        messageLi.innerText = message;
        ul.appendChild(messageLi);
        const refreshHintLi = document.createElement('li');
        refreshHintLi.innerText = 'Please refresh the page to try again.';
        ul.appendChild(refreshHintLi);
        const autoRefreshHintLi = document.createElement('li');
        autoRefreshHintLi.innerText = 'The page will refresh automatically in 30 seconds.';
        ul.appendChild(autoRefreshHintLi);
        if (!hasUl) {
            document.body.appendChild(ul);
        }
        setTimeout(() => location.reload(), 30000);
    }
    setLocalConfig(config) {
        localStorage.setItem(ConfigService.LocalStorageKey, JSON.stringify(config));
    }
    clearLocalConfig() {
        localStorage.removeItem(ConfigService.LocalStorageKey);
    }
    get(propertyPath, defaultValue) {
        return ConfigService.Get(propertyPath, defaultValue, this.config);
    }
    getOrThrow(propertyPath, defaultValue) {
        const value = ConfigService.Get(propertyPath, defaultValue, this.config);
        if (value === undefined) {
            throw new Error(`Could not find config in path '${propertyPath}'`);
        }
        return value;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: ConfigService, deps: [{ token: RXAP_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: ConfigService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.3", ngImport: i0, type: ConfigService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [{ type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [RXAP_CONFIG]
                }] }] });

function ProvideConfig(config = {}) {
    return {
        provide: RXAP_CONFIG,
        useValue: config,
    };
}

// region 
// endregion

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

export { ConfigLoaderService, ConfigService, ConfigTestingService, ProvideConfig, RXAP_CONFIG };
//# sourceMappingURL=rxap-config.mjs.map
