import { error } from '../model/error.js';

export const getToken = async ({
    console, profile, fetch,
    username, password,
    issuerUrl, code, codeVerifier, clientId, redirectUri,
    serviceKey
}) => {
    const apptid = profile.apptid;
    if(serviceKey) {
        if (!serviceKey.uaa) {
            const err = new Error('Can not get token. The uaa property is missing in service key.');
            throw err;
        }
        ['url', 'clientid', 'clientsecret'].forEach(property => {
            if (!serviceKey.uaa[property]) {
                const err = new Error(`Can not get token. The uaa.${property} property is missing in service key.`);
                throw err;
            }
        });
        const loginUrl = new URL(serviceKey.uaa.url);
        loginUrl.pathname = '/oauth/token';
        loginUrl.searchParams.set('apptid', apptid);
        const form = new FormData();
        if (code) {
            form.append('grant_type', 'authorization_code');
            form.append('code', code);
            form.append('redirect_uri', redirectUri);

        } else {
            form.append('grant_type', 'client_credentials');
        }
        console.debug(`Getting token with ${form.get('grant_type')} grant from: ${String(loginUrl)}`);
        const res = await fetch(loginUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Authorization': 'Basic ' +
                    Buffer.from(
                        serviceKey.uaa.clientid + ':' + serviceKey.uaa.clientsecret,
                        'utf8'
                    ).toString('base64')
            },
            body: new URLSearchParams(form).toString()
        });
        if (res.status !== 200) {
            const err = new Error(
                'Failed to get token due to server error: ' +
                `${res.status} ${res.statusText} ${await res.text()}`
            );
            throw err;
        }
        return await res.json();
    }

    if (username || password) {
        const serverUrl = profile?.server_url;
        if (!serverUrl) {
            const err = new Error('Can not get token. Server URL is missing.');
            throw err;
        }
        if (typeof username === 'undefined') {
            const err = new Error('Can not get token. Username is missing.');
            throw err;
        }
        if (typeof password === 'undefined') {
            const err = new Error('Can not get token. Password is missing.');
            throw err;
        }
        const loginUrl = new URL(serverUrl);
        loginUrl.pathname = '/oauth/token';
        loginUrl.searchParams.set('apptid', profile.apptid);
        console.debug(`Getting token with password grant from: ${String(loginUrl)}`);
        const res = await fetch(loginUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Authorization': 'Basic ' + Buffer.from(username + ':' + password, 'utf8').toString('base64')
            }
        });
        if (res.status !== 200) {
            const err = new Error(
                'Failed to get token due to server error: ' +
                `${res.status} ${res.statusText} ${await res.text()}`
            );
            throw err;
        }
        return await res.json();
    }

    if (issuerUrl || code || codeVerifier || clientId || redirectUri || apptid) {
        if (typeof issuerUrl === 'undefined') {
            const err = new Error('Can not get token. Authorization server URL is missing.');
            throw err;
        }
        if (typeof code === 'undefined') {
            const err = new Error('Can not get token. Authorization code is missing.');
            throw err;
        }
        if (typeof codeVerifier === 'undefined') {
            const err = new Error('Can not get token. Code verifier is missing.');
            throw err;
        }
        if (typeof clientId === 'undefined') {
            const err = new Error('Can not get token. Client ID is missing.');
            throw err;
        }
        if (typeof redirectUri === 'undefined') {
            const err = new Error('Can not get token. Redirect URI is missing.');
            throw err;
        }
        if (!apptid) {
            const err = new Error('Can not get token. Application tenant ID is missing.');
            throw err;
        }
        const loginUrl = new URL(issuerUrl);
        loginUrl.pathname = '/oauth2/token';
        loginUrl.searchParams.set('app_tid', apptid);
        loginUrl.searchParams.set('refresh_expiry', '0');  // avoids IAS parallel refresh tokens limits
        const form = new FormData();
        form.append('grant_type', 'authorization_code');
        form.append('client_id', clientId);
        form.append('redirect_uri', redirectUri);
        form.append('code', code);
        form.append('code_verifier', codeVerifier);

        console.debug(`Getting token with autorization code grant (PKCE) from: ${String(loginUrl)}`);
        const res = await fetch(loginUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: new URLSearchParams(form).toString()
        });
        if (res.status !== 200) {
            throw error(
                `Failed to get token due to server error: ${res.status} ${res.statusText} ` +
                await res.text(),
                res.status
            );
        }
        return await res.json();
    }

    const err = new Error(
        'Can not get token. ' +
        'Either password or service key or code grant parameters should be passed'
    );
    throw err;
};
