import { isClient as isClient$1, createClient as createClient$1, stringifyAuthorizationHeader, isObject, HeaderName, Client, hasOwnProperty as hasOwnProperty$1, verifyInstanceBySymbol } from 'hapic';
export { isClientError, isClientErrorDueNetworkIssue, isClientErrorWithStatusCode } from 'hapic';

/*
 * Copyright (c) 2022.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ var AuthorizeResponseMode;
(function(AuthorizeResponseMode) {
    AuthorizeResponseMode["QUERY"] = 'query';
    AuthorizeResponseMode["FRAGMENT"] = 'fragment';
    AuthorizeResponseMode["FORM_POST"] = 'form_post';
    AuthorizeResponseMode["WEB_MESSAGE"] = 'web_message';
})(AuthorizeResponseMode || (AuthorizeResponseMode = {}));

/*
 * Copyright (c) 2021.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ // eslint-disable-next-line @typescript-eslint/ban-types
function hasOwnProperty(obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
}

/*
 * Copyright (c) 2023.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ function scopeToArray(scope) {
    return Array.isArray(scope) ? scope : scope.split(' ');
}
function scopeToString(scope) {
    if (typeof scope === 'string') {
        return scope;
    }
    return [
        ...new Set(scope)
    ].join(' ');
}

class BaseAPI {
    // -----------------------------------------------------------------------------------
    setClient(input) {
        this.client = isClient$1(input) ? input : createClient$1(input);
    }
    setOptions(options) {
        this.options = options || {};
    }
    setOption(key, value) {
        this.options[key] = value;
    }
    // -----------------------------------------------------------------------------------
    constructor(context){
        context = context || {};
        this.setClient(context.client);
        this.setOptions(context.options);
    }
}

class AuthorizeAPI extends BaseAPI {
    /**
     * Build authorize url based on
     * input parameters.
     *
     * @param parameters
     */ buildURL(parameters) {
        parameters = parameters || {};
        let baseURL;
        let input;
        if (this.options.authorizationEndpoint) {
            input = this.options.authorizationEndpoint;
        } else {
            const clientURL = this.client.defaults.baseURL;
            if (clientURL) {
                baseURL = clientURL;
            }
        }
        const url = new URL(input || 'authorize', baseURL);
        url.searchParams.set('response_type', parameters.response_type || 'code');
        if (this.options.clientId) {
            url.searchParams.set('client_id', this.options.clientId);
        }
        const redirectUri = parameters.redirect_uri || this.options.redirectUri;
        if (redirectUri) {
            url.searchParams.set('redirect_uri', redirectUri);
        }
        const scope = [];
        if (this.options.scope) {
            const input = scopeToArray(this.options.scope);
            scope.push(...input);
        }
        if (parameters.scope) {
            const input = scopeToArray(parameters.scope);
            scope.push(...input);
        }
        if (scope.length > 0) {
            url.searchParams.set('scope', scopeToString(scope));
        }
        return url.href;
    }
    // eslint-disable-next-line no-useless-constructor,@typescript-eslint/no-useless-constructor
    constructor(context){
        super(context);
    }
}

/**
 * Set Content-Type and Authorization Header for request.
 *
 * @param headers
 * @param options
 */ function transformHeadersForTokenAPIRequest(headers, options) {
    options = options || {};
    // set content type
    headers.set('Content-Type', 'application/x-www-form-urlencoded');
    if (options.authorizationHeaderInherit && headers.has('Authorization')) {
        return;
    }
    // request should be in general unauthorized
    headers.delete('Authorization');
    if (options.authorizationHeader) {
        const header = typeof options.authorizationHeader === 'string' ? options.authorizationHeader : stringifyAuthorizationHeader(options.authorizationHeader);
        headers.set('Authorization', header);
        return;
    }
    if (options.clientCredentialsAsHeader && options.clientId && options.clientSecret) {
        headers.set('Authorization', stringifyAuthorizationHeader({
            type: 'Basic',
            username: options.clientId,
            password: options.clientSecret
        }));
    }
}

function createRequestTransformerForTokenAPIRequest(parameters, options) {
    return (data, headers)=>{
        options = options || {};
        if (!options.clientId) {
            options.clientId = parameters.client_id;
            options.clientSecret = parameters.client_secret;
        }
        transformHeadersForTokenAPIRequest(headers, options);
        if (options.clientCredentialsAsHeader) {
            if (data instanceof URLSearchParams) {
                data.delete('client_id');
                data.delete('client_secret');
                return data;
            }
            if (isObject(data)) {
                if (typeof data.client_id !== 'undefined') {
                    delete data.client_id;
                }
                if (typeof data.client_secret !== 'undefined') {
                    delete data.client_secret;
                }
            }
        }
        return data;
    };
}

class TokenAPI extends BaseAPI {
    // ------------------------------------------------------------------
    async createWithRefreshToken(parameters, options) {
        return this.create({
            grant_type: 'refresh_token',
            ...parameters
        }, options);
    }
    async createWithClientCredentials(parameters, options) {
        return this.create({
            grant_type: 'client_credentials',
            ...parameters || {}
        }, options);
    }
    async createWithPasswordGrant(parameters, options) {
        return this.create({
            grant_type: 'password',
            ...parameters
        }, options);
    }
    async createWithAuthorizeGrant(parameters, options) {
        return this.create({
            grant_type: 'authorization_code',
            ...parameters
        }, options);
    }
    async createWithRobotCredentials(parameters, options) {
        return this.create({
            grant_type: 'robot_credentials',
            ...parameters
        }, options);
    }
    // ------------------------------------------------------------------
    /**
     * @throws Error
     * @param parameters
     * @param options
     */ async create(parameters, options) {
        options = options || {};
        this.extendCreateParameters(parameters);
        const urlSearchParams = this.buildURLSearchParams(parameters);
        const { data } = await this.client.post(this.options.tokenEndpoint || '/token', urlSearchParams, {
            transform: this.buildRequestTransformers(parameters, options),
            headers: {
                [HeaderName.ACCEPT]: 'application/json'
            }
        });
        const tokenResponse = {
            access_token: data.access_token,
            expires_in: data.expires_in,
            token_type: data.token_type || 'Bearer'
        };
        if (data.refresh_token) {
            tokenResponse.refresh_token = data.refresh_token;
        }
        if (typeof data.id_token === 'string') {
            tokenResponse.id_token = data.id_token;
        }
        if (typeof data.mac_key === 'string') {
            tokenResponse.mac_key = data.mac_key;
        }
        if (typeof data.mac_algorithm === 'string') {
            tokenResponse.mac_algorithm = data.mac_algorithm;
        }
        return tokenResponse;
    }
    async introspect(parameters, options) {
        options = options || {};
        const urlSearchParams = this.buildURLSearchParams(parameters);
        const { data } = await this.client.post(this.options.introspectionEndpoint || '/token/introspect', urlSearchParams, {
            transform: this.buildRequestTransformers(parameters, options),
            headers: {
                [HeaderName.ACCEPT]: 'application/json'
            }
        });
        return data;
    }
    buildRequestTransformers(parameters, options) {
        const transformers = [];
        options = options || {};
        if (!options.clientId) {
            if (this.options.clientId) {
                options.clientId = this.options.clientId;
            }
            if (this.options.clientSecret) {
                options.clientSecret = this.options.clientSecret;
            }
        }
        transformers.push(createRequestTransformerForTokenAPIRequest(parameters, options));
        if (this.client.defaults.transform) {
            if (Array.isArray(this.client.defaults.transform)) {
                transformers.push(...this.client.defaults.transform);
            } else {
                transformers.push(this.client.defaults.transform);
            }
        }
        return transformers;
    }
    // ------------------------------------------------------------------
    extendCreateParameters(parameters) {
        if (parameters.grant_type !== 'authorization_code' && parameters.grant_type !== 'robot_credentials') {
            if (!parameters.scope && this.options.scope) {
                parameters.scope = this.options.scope;
            }
        }
        if (parameters.grant_type === 'authorization_code') {
            if (!parameters.redirect_uri && this.options.redirectUri) {
                parameters.redirect_uri = this.options.redirectUri;
            }
        }
        if (!parameters.client_id) {
            if (parameters.client_secret) {
                delete parameters.client_secret;
            }
            if (this.options.clientId) {
                parameters.client_id = this.options.clientId;
            }
            if (this.options.clientSecret) {
                parameters.client_secret = this.options.clientSecret;
            }
        }
        return parameters;
    }
    // ------------------------------------------------------------------
    buildURLSearchParams(input) {
        const urlSearchParams = new URLSearchParams();
        const keys = Object.keys(input);
        for(let i = 0; i < keys.length; i++){
            const value = input[keys[i]];
            if (typeof value === 'string' && !!value) {
                urlSearchParams.append(keys[i], value);
            } else if (Array.isArray(value)) {
                const str = value.filter((el)=>el).join(' ');
                if (str) {
                    urlSearchParams.append(keys[i], str);
                }
            }
        }
        return urlSearchParams;
    }
    // eslint-disable-next-line no-useless-constructor,@typescript-eslint/no-useless-constructor
    constructor(context){
        super(context);
    }
}

class UserInfoAPI extends BaseAPI {
    // -----------------------------------------------------------------------------------
    /**
     * @throws Error
     * @param header
     */ async get(header) {
        const headers = {
            [HeaderName.ACCEPT]: 'application/json'
        };
        if (header) {
            if (typeof header === 'string') {
                if (header.indexOf(' ') === -1) {
                    headers.Authorization = `Bearer ${header}`;
                } else {
                    headers.Authorization = header;
                }
            } else {
                headers.Authorization = stringifyAuthorizationHeader(header);
            }
        }
        const { data } = await this.client.get(this.options.userinfoEndpoint || '/userinfo', {
            headers
        });
        return data;
    }
    // eslint-disable-next-line no-useless-constructor,@typescript-eslint/no-useless-constructor
    constructor(context){
        super(context);
    }
}

class OAuth2Client extends Client {
    // -----------------------------------------------------------------------------------
    constructor(input){
        input = input || {};
        super(input.request);
        this['@instanceof'] = Symbol.for('OAuth2Client');
        const options = input.options || {};
        this.options = options;
        this.token = new TokenAPI({
            client: this,
            options
        });
        this.authorize = new AuthorizeAPI({
            client: this,
            options
        });
        this.userInfo = new UserInfoAPI({
            client: this,
            options
        });
    }
}

const instanceMap = {};
/**
 * Verify if an oauth2 client singleton instance exists.
 *
 * @param key
 */ function hasClient(key) {
    return hasOwnProperty$1(instanceMap, key || 'default');
}
/**
 * Set the oauth2 client singleton instance.
 *
 * @param client
 * @param key
 */ function setClient(client, key) {
    key = key || 'default';
    instanceMap[key] = client;
    return client;
}
/**
 * Receive an oauth2 singleton instance.
 *
 * @param key
 */ function useClient(key) {
    key = key || 'default';
    if (Object.prototype.hasOwnProperty.call(instanceMap, key)) {
        return instanceMap[key];
    }
    const instance = createClient();
    instanceMap[key] = instance;
    return instance;
}
/**
 * Unset an oauth2 client singleton instance.
 *
 * @param key
 */ function unsetClient(key) {
    key = key || 'default';
    if (hasOwnProperty$1(instanceMap, key)) {
        delete instanceMap[key];
    }
}
/**
 * Create an oauth2 client.
 *
 * @param input
 */ function createClient(input) {
    return new OAuth2Client(input);
}
/**
 * Check if the argument is of instance Client.
 *
 * @param input
 */ function isClient(input) {
    if (input instanceof OAuth2Client) {
        return true;
    }
    return verifyInstanceBySymbol(input, 'OAuth2Client');
}

/*
 * Copyright (c) 2023.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ var ResponseType;
(function(ResponseType) {
    ResponseType["NONE"] = 'none';
    ResponseType["CODE"] = 'code';
    ResponseType["ID_TOKEN"] = 'id_token';
    ResponseType["TOKEN"] = 'token';
})(ResponseType || (ResponseType = {}));

/*
 * Copyright (c) 2023.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ function parseOpenIDProviderMetadata(data) {
    const options = {};
    if (data.authorization_endpoint) {
        options.authorizationEndpoint = data.authorization_endpoint;
    }
    if (data.introspection_endpoint) {
        options.introspectionEndpoint = data.introspection_endpoint;
    }
    if (data.token_endpoint) {
        options.tokenEndpoint = data.token_endpoint;
    }
    if (data.userinfo_endpoint) {
        options.userinfoEndpoint = data.userinfo_endpoint;
    }
    return options;
}

/**
 * Create a client instance by requesting details of
 * the open-id discovery endpoint.
 *
 * @param url (.e.g. .well-known/openid-configuration)
 * @param options
 */ async function createClientWithOpenIDDiscoveryURL(url, options) {
    let client;
    if (isClient$1(options)) {
        client = options;
    } else {
        client = createClient$1(options);
    }
    const { data } = await client.get(url);
    return new OAuth2Client({
        request: client.defaults,
        options: parseOpenIDProviderMetadata(data)
    });
}

/*
 * Copyright (c) 2023.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */ function buildOpenIDDiscoveryURL(baseURL) {
    let url = '.well-known/openid-configuration';
    if (baseURL) {
        url = new URL(url, baseURL).href;
    } else {
        url = `/${url}`;
    }
    return url;
}

const client = createClient();

export { AuthorizeAPI, AuthorizeResponseMode, OAuth2Client, ResponseType, TokenAPI, UserInfoAPI, buildOpenIDDiscoveryURL, createClient, createClientWithOpenIDDiscoveryURL, client as default, hasClient, hasOwnProperty, isClient, parseOpenIDProviderMetadata, scopeToArray, scopeToString, setClient, unsetClient, useClient };
//# sourceMappingURL=index.mjs.map
