// @ts-check
const { request, LRU, getInstance } = require('./common');

const EndPoint = 'https://api-compute.cloud.toast.com/compute';

class Toast {

    /**
     * @typedef {object} TokenStore
     * @property {TokenStoreGet} get
     * @property {TokenStoreSet} set
     * @memberof Toast
     */

    /**
     * @typedef {function} TokenStoreGet
     * @callback 
     * @param {string} key
     * @returns {string} token.id
     * @memberof Toast
     */

    /**
     * @typedef {function} TokenStoreSet
     * @callback
     * @param {string} key
     * @param {string} id token.id
     * @param {number} expireRemainMs
     * @returns {void}
     * @memberof Toast
     */

    /**
     * @typedef {object} CommonHeaders
     * @property {string} X-Auth-Token
     */

    /**
     * Create new Toast Instance
     * @param {object} authInfo
     * @param {string} authInfo.TOAST_ID
     * @param {string} authInfo.APPKEY
     * @param {string} authInfo.TENANT_ID
     * @param {string} authInfo.API_PASSWORD
     * @param {TokenStore} [authInfo.tokenStore]
     */
    constructor({ TOAST_ID, APPKEY, TENANT_ID, API_PASSWORD, tokenStore }) {

        this.TOAST_ID = TOAST_ID;
        this.APPKEY = APPKEY;
        this.TENANT_ID = TENANT_ID;
        this.API_PASSWORD = API_PASSWORD;

        this.tokenStore = tokenStore || new LRU();

    }

    /**
     * @param {string} url 
     * @param {object} [options]
     * @return {Promise<object>}
     */
    async request(url, options = {}) {

        options.headers = {
            'Content-Type': 'application/json;charset=UTF-8',
            ...options.headers,
        };

        /** @type {{header: object}} */
        const { header, ...result } = await request({
            method: 'POST',
            url,
            json: true,
            ...options,
        });

        if (!header.isSuccessful) throw new Error(`[${header.resultCode}] ${header.resultMessage}`);

        return result;

    }

    /**
     * Compute / Network 등에서 사용하는 엑세스 토큰 헤더 획득
     * @returns {Promise<CommonHeaders>}
     */
    async getCommonHeaders() {

        /** @type {string} */
        const token = await this.getCommonToken();

        // @ts-ignore 현재 VSCode TS 파서가 - 이 포함된 @property를 제대로 처리하지 못함
        return { 'X-Auth-Token': token };

    }

    /**
     * Compute / Network 등에서 사용하는 일반 토큰 획득
     * @returns {Promise<string>} token.id
     */
    async getCommonToken() {

        const curToken = await this.tokenStore.get('ToastCommonToken');

        if (curToken) return curToken; // 토큰 재사용

        const path = 'tokens';
        const username = this.TOAST_ID;
        const password = this.API_PASSWORD;
        const body = { auth: { username, password } };

        const { access: { token, user }, ...r } = await this.request(`${EndPoint}/v1.0/appkeys/${this.APPKEY}/${path}`, { body });
        const expireRemainMs = +new Date(token.expires) - +new Date() - (1000 * 60 * 5); // 토큰 만료 5분전까지 유효

        await this.tokenStore.set('ToastCommonToken', token.id, expireRemainMs);

        return token.id;

    }

    get compute() { return getInstance(this, 'compute', require('./Compute')); }
    get network() { return getInstance(this, 'network', require('./Network')); }

}

module.exports = Toast;