import * as cypress from "cypress"
var globalVars = {};
interface response {
    status: number,
    body: {
        message: string,
        reaponseData: object
    },
    entity?: execution
}
interface execution {
    title: string;
    entity: string;
    method: string;
    failOnStatusCode: false;
    params: object;
    url: string;
    headers: object;
    qs?: object;
    payload?: object;
    data?: object;
    status: number;
    follow: Array<execution>;
    as: string;
    storeVariables: string;
    expectedResponseCode: number;
}

const exection = (requestData: any, test) => {
    cy.request(requestData).then((entity) => {
        cy.log("Response: ", JSON.stringify(entity.body))
        if (entity.requestHeaders['content-type'] && entity.requestHeaders['content-type'].startsWith('multipart/form-data')) {
            const bodyString = Cypress.Blob.arrayBufferToBinaryString(entity.body);
            const body = JSON.parse(bodyString);
            entity.body = body;
        }
        expect(entity.status).to.eq(test.expectedResponseCode);
        if (test?.storeVariables) {
            let reqresp = typeof entity?.body?.responseData != 'undefined' ? entity.body.responseData : entity.body;
            for (let rv of test['storeVariables']) {
                let varTypeParts = rv.split("|")
                let type = '';
                if (varTypeParts.length > 1) {
                    type = varTypeParts[1]
                    varTypeParts.pop(); // Removes the last element (4)
                    rv = varTypeParts.join(':');
                }
                let varParts = rv.split(":")
                if (varParts.length == 1) {
                    if (type == 'int') {
                        reqresp[rv] = parseInt(reqresp[rv]);
                    }
                    else if (type == 'float') {
                        reqresp[rv] = parseFloat(reqresp[rv]);
                    }
                    globalVars[test.entity][rv] = reqresp[rv]
                }
                else {
                    if (varParts.length == 2) {
                        globalVars[test.entity][varParts[1]] = reqresp[varParts[0]]
                    }
                    else {
                        var entitykey = varParts.pop();
                        let value = reqresp;
                        varParts.map(key => {
                            if (type == 'int') {
                                value[key] = parseInt(value[key]);
                            }
                            else if (type == 'float') {
                                value[key] = parseFloat(value[key]);
                            }
                            value = value[key]
                        })
                        globalVars[test.entity][entitykey] = value;
                    }
                }
            }
        }
        if ('follow' in test) {
            if (test.follow.length > 0 && (entity.status == 200 || entity.status == 201)) {
                for (const follow of test.follow) {
                    let nextRequest = follow;
                    if (nextRequest?.headers) {
                        for (const key in nextRequest.headers) {
                            if (typeof nextRequest.headers[key] == 'number') {
                                nextRequest.headers[key] = nextRequest.headers[key]
                                globalVars[test.entity][key] = nextRequest.headers[key];
                            } else if (typeof nextRequest.headers[key] == 'boolean') {
                                nextRequest.headers[key] = nextRequest.headers[key]
                                globalVars[test.entity][key] = nextRequest.headers[key];
                            }
                            else if (nextRequest.headers[key].startsWith(":")) {
                                let useKey = nextRequest.headers[key].replace(":", "");
                                nextRequest.headers[key] = globalVars[test.entity][useKey];
                                globalVars[test.entity][key] = globalVars[test.entity][useKey];
                            }
                            else if (nextRequest.headers[key] == '') {
                                nextRequest.headers[key] = globalVars[test.entity][key]
                            }
                        }
                    }
                    if (nextRequest?.params) {
                        for (const key in nextRequest.params) {
                            if (typeof nextRequest.params[key] == 'number') {
                                nextRequest.params[key] = nextRequest.params[key];
                                globalVars[test.entity][key] = nextRequest.params[key];
                            } else if (typeof nextRequest.params[key] == 'boolean') {
                                nextRequest.params[key] = nextRequest.params[key];
                                globalVars[test.entity][key] = nextRequest.params[key];
                            }
                            else if (nextRequest.params[key].startsWith(":")) {
                                let useKey = nextRequest.params[key].replace(":", "");
                                nextRequest.params[key] = globalVars[test.entity][useKey];
                                globalVars[test.entity][key] = globalVars[test.entity][useKey];
                            }
                            else if (nextRequest.params[key] == '') {
                                nextRequest.params[key] = globalVars[test.entity][key]
                            } else if (nextRequest.params[key].match('/{(.*?)}/') && nextRequest.params[key].match('/{(.*?)}/').length) {
                                let g = nextRequest.params[key].match('/{(.*?)}/')[0];
                                let newKey = g.substring(1, g.length - 1)
                                nextRequest.params[newKey] = globalVars[test.entity][key]
                            }
                        }
                    }
                    if (nextRequest?.qs) {
                        for (const key in nextRequest.qs) {
                            if (typeof nextRequest.qs[key] == 'number') {
                                nextRequest.qs[key] = nextRequest.qs[key];
                                globalVars[test.entity][key] = nextRequest.qs[key];
                            } else if (typeof nextRequest.qs[key] == 'boolean') {
                                nextRequest.data[key] = nextRequest.data[key];
                                globalVars[test.entity][key] = nextRequest.data[key];
                            }
                            else if (nextRequest.qs[key].startsWith(":")) {
                                let useKey = nextRequest.qs[key].replace(":", "");
                                nextRequest.qs[key] = globalVars[test.entity][useKey];
                                globalVars[test.entity][key] = globalVars[test.entity][useKey];
                            }
                            else if (nextRequest.qs[key] == '') {
                                nextRequest.qs[key] = globalVars[test.entity][key]
                            } else if (nextRequest.qs[key].match('/{(.*?)}/') && nextRequest.qs[key].match('/{(.*?)}/').length) {
                                let g = nextRequest.qs[key].match('/{(.*?)}/')[0];
                                let newKey = g.substring(1, g.length - 1)
                                nextRequest.qs[newKey] = globalVars[test.entity][key]
                            }
                        }
                    } else {
                        if (nextRequest.data) {
                            for (const key in nextRequest.data) {
                                if (typeof nextRequest.data[key] == 'number') {
                                    nextRequest.data[key] = nextRequest.data[key];
                                    globalVars[test.entity][key] = nextRequest.data[key];
                                } else if (typeof nextRequest.data[key] == 'boolean') {
                                    nextRequest.data[key] = nextRequest.data[key];
                                    globalVars[test.entity][key] = nextRequest.data[key];
                                } else if (typeof nextRequest.data[key] == 'object') {
                                    for (let arrayval of nextRequest.data[key]) {
                                        if (typeof arrayval == 'string') {
                                            if (arrayval.startsWith(":")) {
                                                let useKey = arrayval.replace(":", "");
                                                nextRequest.data[key] = globalVars[test.entity][useKey];
                                                globalVars[test.entity][key] = globalVars[test.entity][useKey];
                                            }
                                        } else {
                                            globalVars[test.entity][key] = arrayval
                                        }
                                    }
                                }
                                else if (nextRequest.data[key].startsWith(":")) {
                                    let useKey = nextRequest.data[key].replace(":", "");
                                    nextRequest.data[key] = globalVars[test.entity][useKey];
                                    globalVars[test.entity][key] = globalVars[test.entity][useKey];
                                }
                                else if (nextRequest.data[key] == '') {
                                    nextRequest.data[key] = globalVars[test.entity][key]
                                } else if (nextRequest.data[key].match('/{(.*?)}/') && nextRequest.data[key].match('/{(.*?)}/').length) {
                                    let g = nextRequest.data[key].match('/{(.*?)}/')[0];
                                    let newKey = g.substring(1, g.length - 1)
                                    nextRequest.data[newKey] = globalVars[test.entity][key]
                                }
                            }
                        }
                    }
                    execute(nextRequest)
                }
            }
        }
    })
}

const execute = (test: execution) => {
    if (typeof globalVars[test.entity] == 'undefined') {
        globalVars[test.entity] = {};
    }
    let failOnStatusCode = true;
    if ('failOnStatusCode' in test) {
        failOnStatusCode = test?.failOnStatusCode
    }
    if (test?.headers) {
        for (const key in test.headers) {
            if (test.headers[key].startsWith(":")) {
                let useKey = test.headers[key].replace(":", "");
                test.headers[key] = globalVars[test.entity][useKey];
                test[test.entity][key] = globalVars[test.entity][useKey];
            }
            else if (test.headers[key] == '') {
                test.headers[key] = globalVars[test.entity][key]
            }
        }
    }
    if (test.params) {
        for (const key in test.params) {
            if (typeof test.params[key] == 'number') {
                test.params[key] = test.params[key]
            } else if (typeof test.params[key] == 'boolean') {
                test.params[key] = test.params[key]
            }
            else if (test.params[key] == '') {
                test.params[key] = globalVars[test.entity][key]
            } else if (test.params[key].startsWith(":")) {
                let useKey = test.params[key].replace(":", "");
                test.params[key] = globalVars[test.entity][useKey];
                //globalVars[test.entity][key] = globalVars[test.entity][useKey];

            } else if (test.params[key].match('/{(.*?)}/') && test.params[key].match('/{(.*?)}/').length) {
                let g = test.params[key].match('/{(.*?)}/')[0];
                let newKey = g.substring(1, g.length - 1)
                test.params[newKey] = globalVars[test.entity][key]
            }
        }
        for (const params in test.params) {
            test.url = test?.url.replace('{' + params + '}', test.params[params])
        }
    }
    if (test.data) {
        for (let key in test.data) {
            if (test.data[key] && typeof test.data[key] != 'number' && test.data[key] && typeof test.data[key] != 'boolean') {
                if (typeof test.data[key] == 'object') {
                    if (typeof test.data[key] === 'object' && Array.isArray(test.data[key])) {
                        test.data[key] = test.data[key].map((arrayval) => {
                            if (typeof arrayval == 'string') {
                                if (arrayval.match(/{(.*?)}/)) {
                                    let keyToChangeWith = arrayval;
                                    keyToChangeWith = keyToChangeWith.replace(/[{}]/g, "").trim();
                                    return globalVars[test.entity][keyToChangeWith];
                                }
                            }
                            return arrayval;
                        });
                    }
                }
                else if (test.data[key].match(/{(.*?)}/) && test.data[key].match(/{(.*?)}/).length) {
                    let keyToChangeWith = test.data[key];
                    keyToChangeWith = keyToChangeWith.replace(/[{}]/g, "");
                    test.data[key] = globalVars[test.entity][keyToChangeWith]
                }
            }
        }
    }
    cy.log("========================================================================");
    cy.log(`🧪 Subtest: ${test.title} (${test.as})`)
    cy.log("========================================================================");
    cy.log("Request: ", JSON.stringify({ title: test.title, url: test.url, headers: test.headers, method: test.method, parameters: test.params, payload: test.data, query: test.qs }));
    let requestData = {};
    let fileUpload = false;
    if (test?.qs) {
        requestData = { url: test.url, method: test.method, qs: test.qs, headers: test.headers, failOnStatusCode: failOnStatusCode }
    } else {
        if (test.data && 'fileName' in test.data) {
            fileUpload = true;
            let file: string = test.data.fileName as unknown as string;
            let fileData = cy.fixture(file, 'binary').then((fileData) => {
                const blob = Cypress.Blob.binaryStringToBlob(fileData);
                const formData = new FormData();
                formData.append('file', blob, file);
                requestData = { method: test.method, url: test.url, headers: test.headers, body: formData, timeout: 300000 }
                exection(requestData, test)
            })

        } else {
            requestData = { url: test.url, method: test.method, body: test.data, headers: test.headers, failOnStatusCode: failOnStatusCode }
        }
    }
    if (!fileUpload) {
        exection(requestData, test)
    }
}
export { execute }