/* eslint-disable @typescript-eslint/no-unused-vars */
import { PostmanTransformer, dataValue, paramValue } from './PostmanTransformer.js';
import { HttpProject, Kind as HttpProjectKind } from '../HttpProject.js';
import { nanoid } from '../../nanoid.js';
import { Environment } from '../Environment.js';
import { Thing } from '../Thing.js';
export const currentItemValue = Symbol('currentItemValue');
/**
 * Transforms Postman v2 collections to ARC import object.
 */
export class PostmanV2Transformer extends PostmanTransformer {
    /**
     * Transforms `_data` into ARC data model.
     * @returns Promise resolved when data are transformed.
     */
    async transform() {
        const raw = this[dataValue];
        const project = this.createProject(raw);
        this.setProjectVariables(project, raw);
        if (Array.isArray(raw.item)) {
            for (const item of raw.item) {
                await this.addProjectItem(project, item);
            }
        }
        return project;
    }
    createProject(data) {
        const { info } = data;
        const init = {
            kind: HttpProjectKind,
            definitions: {},
            info: Thing.fromName(info.name || 'Unnamed Postman project').toJSON(),
            items: [],
            key: info._postman_id || nanoid(),
        };
        if (info.description) {
            init.info.description = info.description;
        }
        const project = new HttpProject(init);
        return project;
    }
    setProjectVariables(project, collection) {
        const { variable } = collection;
        if (!Array.isArray(variable)) {
            return;
        }
        const env = Environment.fromName('Default');
        project.addEnvironment(env);
        variable.forEach((param) => {
            const { disabled = false, key, value } = param;
            const parsed = this.ensureVariablesSyntax(value);
            const created = env.addVariable(key, parsed);
            created.enabled = !disabled;
        });
    }
    async addProjectItem(current, item) {
        const typedGroup = item;
        if (typedGroup.item) {
            return this.addProjectFolder(current, typedGroup);
        }
        const typedItem = item;
        if (typedItem.request) {
            return this.addProjectRequest(current, item);
        }
        this.addLog('error', 'Error when adding an item to the project. Unable to determine item type. Tested for a folder and a request.');
    }
    async addProjectFolder(current, group) {
        const { description, name = 'Unnamed folder', item } = group;
        const created = current.addFolder(name);
        if (description) {
            created.info.description = description;
        }
        if (Array.isArray(item)) {
            for (const tmp of item) {
                await this.addProjectItem(created, tmp);
            }
        }
    }
    async addProjectRequest(current, item) {
        const { request, description } = item;
        const name = item.name || 'Unnamed request';
        let url;
        if (typeof request.url === 'string') {
            url = request.url;
        }
        else if (request.url && request.url.raw) {
            url = request.url.raw;
        }
        else {
            url = 'http://';
        }
        url = this.ensureVariablesSyntax(url);
        let method = request.method || 'GET';
        method = this.ensureVariablesSyntax(method);
        const header = this.ensureVarsRecursively(request.header);
        const headers = this.computeHeaders(header);
        const created = current.addRequest(url);
        created.info.name = name;
        if (request.description) {
            created.info.description = request.description;
        }
        else if (description) {
            created.info.description = description;
        }
        created.expects.method = method;
        if (headers) {
            created.expects.headers = headers;
        }
        await this.addRequestBody(created, request.body);
    }
    /**
     * Computes headers string from item's headers.
     *
     * @param headers Postman Request.header model.
     * @return Computed value of headers.
     */
    computeHeaders(headers) {
        if (typeof headers === 'string') {
            return headers;
        }
        if (!Array.isArray(headers)) {
            return '';
        }
        const tmp = headers.filter((h) => !h.disabled);
        return tmp.map((item) => `${item.key}: ${item.value}`).join('\n');
    }
    async addRequestBody(created, body) {
        if (!body) {
            return;
        }
        switch (body.mode) {
            case 'file':
                return this.addBinaryBody(created, body);
            case 'urlencoded':
                return this.addUrlencodedBody(created, body);
            case 'formdata':
                return this.addParamsBody(created, body);
            case 'raw':
                return this.addRawBody(created, body);
        }
    }
    async addBinaryBody(created, body) {
        // Postman sets the `body.file.src` property with the path to the file
        // but not data itself. Because of that this method is a stub that can be extended
        // by particular implementation (like node, CLI).
    }
    async addUrlencodedBody(created, body) {
        const items = body[body.mode];
        if (!Array.isArray(items)) {
            return;
        }
        const data = this.ensureVarsRecursively(items);
        const str = data.map((i) => `${paramValue(i.key || 'missing name')}=${paramValue(i.value || '')}`).join('&');
        await created.expects.writePayload(str);
    }
    async addParamsBody(created, body) {
        const items = body[body.mode];
        if (!Array.isArray(items)) {
            return;
        }
        const data = this.ensureVarsRecursively(items);
        const multipartBody = [];
        for (const part of data) {
            if (part.type === 'text') {
                await this.addParamsTextPart(multipartBody, part);
            }
            else if (part.type === 'file') {
                await this.addParamsFilePart(multipartBody, part);
            }
        }
        created.expects.payload = {
            type: 'formdata',
            data: multipartBody,
        };
    }
    async addParamsTextPart(body, param) {
        const { key, value, disabled = false } = param;
        body.push({
            isFile: false,
            name: key || 'unnamed part',
            value: value || '',
            enabled: !disabled,
        });
    }
    async addParamsFilePart(body, param) {
        // Postman sets the `value` property with the path to the file
        // but not data itself. Because of that this method is a stub that can be extended
        // by particular implementation (like node, CLI).
    }
    async addRawBody(created, body) {
        if (body.raw) {
            await created.expects.writePayload(body.raw);
        }
    }
}
//# sourceMappingURL=PostmanV2Transformer.js.map