import { CodeBuilder } from '../../helpers/code-builder.js';
import { HTTPSnippet } from '../../index.js';
import { Lang } from '../../helpers/constants.js';
import { buildUrlExpression, printUrlVariablesDeclarations } from '../../helpers/code-helpers.js';
import { normalizeMimeType } from '../../../../utils.js';
import { MediaTypes } from '../../../../../constants.js';
const indent = 1;
function hasMimeType(postData = {}, mimeType) {
    return normalizeMimeType(postData.mimeType) === mimeType;
}
const handler = function (source, originalOptions, { target, client }) {
    const options = {
        capitalize: true,
        showBoilerplate: true,
        checkErrors: true,
        printBody: true,
        timeout: -1,
        indent: '  ',
        ...originalOptions,
    };
    const securityOAuth2ExtraCalls = source.securityOAuth2ExtraCalls?.[0];
    const errorPlaceholder = options.checkErrors ? 'err' : '_';
    const reqMethod = source.method.toUpperCase();
    const code = new CodeBuilder({
        indentation: options.indent,
        variablesPrefix: options.variablesPrefix,
        capitalize: options.capitalize,
        lang: Lang.GO,
    });
    const errorCheck = () => {
        if (options.checkErrors) {
            code
                .push(indent, 'if err != nil {')
                .push(indent + 1, 'panic(err)')
                .push(indent, '}');
        }
    };
    // Create boilerplate
    if (options.showBoilerplate) {
        code.push('package main').blank().push('import (').push(indent, '"fmt"');
        if (options.timeout > 0) {
            code.push(indent, '"time"');
        }
        if (securityOAuth2ExtraCalls) {
            code.push(indent, '"encoding/json"');
        }
        if (hasMimeType(source.postData, MediaTypes.MULTIPART)) {
            code.push(indent, '"mime/multipart"');
            code.push(indent, '"io"');
            code.push(indent, '"bytes"');
        }
        if (hasMimeType(source.postData, MediaTypes.URL_ENCODED) ||
            hasMimeType(source.securityOAuth2ExtraCalls?.[0]?.postData, MediaTypes.URL_ENCODED)) {
            code.push(indent, '"net/url"');
            code.push(indent, '"strconv"');
        }
        if (hasMimeType(source.postData, MediaTypes.JSON) ||
            hasMimeType(source.securityOAuth2ExtraCalls?.[0]?.postData, MediaTypes.JSON)) {
            code.push(indent, '"bytes"');
        }
        if ((source.postData && !hasMimeType(source.postData, MediaTypes.JSON)) ||
            securityOAuth2ExtraCalls) {
            code.push(indent, '"strings"');
        }
        code.push(indent, '"net/http"');
        if (options.printBody) {
            code.push(indent, '"io/ioutil"');
        }
        code.push(')').blank().push('func main() {');
    }
    // put headers values into quotation marks
    if (Object.keys(source.allHeaders).length) {
        Object.keys(source.allHeaders).forEach((key) => {
            source.allHeaders[key] = `"${source.allHeaders[key]}"`;
        });
    }
    if (securityOAuth2ExtraCalls) {
        const prefix = 'oAuth2';
        const decoderErrorString = options.checkErrors ? `${errorPlaceholder} := ` : '';
        const resString = options.capitalize ? 'Res' : 'res';
        const oAuth2PrefixCode = new HTTPSnippet(securityOAuth2ExtraCalls).convert(target, client, {
            ...options,
            printBody: false,
            variablesPrefix: prefix,
            showBoilerplate: false,
        });
        code
            .push(oAuth2PrefixCode)
            .blank()
            .push(indent, `var ${prefix}${resString}Body struct {`)
            .push(indent + 1, 'Access_token string')
            .push(indent, '}')
            .push(indent, `${decoderErrorString}json.NewDecoder(${prefix}${resString}.Body).Decode(&${prefix}${resString}Body)`)
            .blank();
        errorCheck();
        source.allHeaders.Authorization = `"Bearer " + ${prefix}${resString}Body.Access_token`;
    }
    printUrlVariablesDeclarations(source, code, indent);
    // Create client
    if (options.timeout > 0) {
        client = code.var('client');
        code
            .push(indent, '%s := http.Client{', client)
            .push(indent + 1, 'Timeout: time.Duration(%s * time.Second),', options.timeout)
            .push(indent, '}')
            .blank();
    }
    // Set URL
    code.push(indent, `${code.var('reqUrl')} := ${buildUrlExpression(source, code)}`);
    if (source.postData) {
        let stringifiedData;
        switch (normalizeMimeType(source.postData.mimeType)) {
            case MediaTypes.URL_ENCODED:
                code.push(indent, '%s := url.Values{}', code.var('data'));
                source.postData.params.forEach((param) => {
                    code.push(indent, '%s.Set("%s", "%s")', code.var('data'), param.name, param.value);
                });
                code.push(indent, '%s, %s := http.NewRequest("%s", %s, strings.NewReader(%s.Encode()))', code.var('req'), errorPlaceholder, reqMethod, code.var('reqUrl'), code.var('data'));
                source.allHeaders['Content-Length'] = `strconv.Itoa(len(${code.var('data')}.Encode()))`;
                break;
            case MediaTypes.JSON:
                /*
                  Note about next stringify:
                  GO buffers only JSON object with double-quotes for both key and value,
                  that is why we are using there JSON.stringify instead of stringify-object package.
                  Also to be more readable we add an extra indentation for each stringified code line.
                 */
                stringifiedData = JSON.stringify(source.postData.jsonObj, null, 2)
                    .split('\n')
                    .map((line) => options.indent + line)
                    .join('\n')
                    .trim(); // remove redundant spaces at the beginning
                code.push(indent, `var ${code.var('data')} = []byte(\`${stringifiedData}\`)`);
                code.push(indent, '%s, %s := http.NewRequest("%s", %s, bytes.NewBuffer(%s))', code.var('req'), errorPlaceholder, reqMethod, code.var('reqUrl'), code.var('data'));
                break;
            case MediaTypes.MULTIPART:
                code
                    .push(indent, '%s := &bytes.Buffer{}', code.var('data'))
                    .push(indent, 'writer := multipart.NewWriter(%s)', code.var('data'));
                source.postData.params.forEach((param) => {
                    if (!param.fileName && !param.contentType) {
                        code
                            .push(indent, '%s, _ := writer.CreateFormField("%s")', code.var(`${param.name}Fw`), param.name)
                            .push(indent, '_, %s = io.Copy(%s, strings.NewReader("%s"))', errorPlaceholder, code.var(`${param.name}Fw`), param.value);
                        errorCheck();
                        return;
                    }
                    if (param.fileName) {
                        code.push(indent, '%s, %s = writer.CreateFormFile("%s", "%s")', code.var(`${param.name}Fw`), errorPlaceholder, param.name, param.fileName);
                        errorCheck();
                    }
                });
                code
                    .push(indent, 'writer.Close()')
                    .blank()
                    .push(indent, '%s := bytes.NewReader(%s.Bytes())', code.var('payload'), code.var('data'))
                    .push(indent, '%s, %s := http.NewRequest("%s", %s, %s)', code.var('req'), errorPlaceholder, reqMethod, code.var('reqUrl'), code.var('payload'));
                source.allHeaders['Content-Type'] = 'writer.FormDataContentType()';
                break;
            default:
                if (source.postData.text) {
                    code
                        .push(indent, '%s := strings.NewReader(%s)', code.var('payload'), JSON.stringify(source.postData?.text))
                        .push(indent, '%s, %s := http.NewRequest("%s", %s, %s)', code.var('req'), errorPlaceholder, reqMethod, code.var('reqUrl'), code.var('payload'));
                }
        }
    }
    else {
        code.push(indent, '%s, %s := http.NewRequest("%s", %s, nil)', code.var('req'), errorPlaceholder, reqMethod, code.var('reqUrl'));
    }
    // Print query variables if present
    const hasQueryVariables = Object.getOwnPropertyNames(source.queryObj).length;
    if (hasQueryVariables) {
        code.blank().push(indent, `${code.var('query')} := ${code.var('req')}.URL.Query()`);
        for (const name in source.queryObj) {
            const value = source.queryObj[name];
            const isArrayString = typeof value === 'string' && /^\[.*\]$/.test(value);
            const queryValue = isArrayString ? `\`${value}\`` : `"${value}"`;
            code.push(indent, `${code.var('query')}.Add("${name}", ${queryValue})`);
        }
        code.push(indent, `${code.var('req')}.URL.RawQuery = ${code.var('query')}.Encode()`).blank();
    }
    errorCheck();
    if (source.basicAuth) {
        const { username, password } = source.basicAuth;
        code.push(indent, `%s.SetBasicAuth("${username}", "${password}")`, code.var('req'));
    }
    // Add headers
    if (Object.keys(source.allHeaders).length) {
        Object.keys(source.allHeaders).forEach((key) => {
            code.push(indent, '%s.Header.Add("%s", %s)', code.var('req'), key, source.allHeaders[key]);
        });
    }
    // Make request
    code.push(indent, '%s, %s := %s.Do(%s)', code.var('res'), errorPlaceholder, client, code.var('req'));
    errorCheck();
    // Get Body
    if (options.printBody) {
        code
            .push(indent, 'defer %s.Body.Close()', code.var('res'))
            .push(indent, '%s, %s := ioutil.ReadAll(%s.Body)', code.var('body'), errorPlaceholder, code.var('res'));
        errorCheck();
    }
    // Print it
    code.blank().push(indent, 'fmt.Println(%s)', code.var('res'));
    if (options.printBody) {
        code.push(indent, 'fmt.Println(string(%s))', code.var('body'));
    }
    // End main block
    if (options.showBoilerplate) {
        code.push('}');
    }
    return code.join();
};
export const info = {
    key: 'native',
    title: 'NewRequest',
    link: 'http://golang.org/pkg/net/http/#NewRequest',
    description: 'Golang HTTP client request',
};
export default handler;
//# sourceMappingURL=native.js.map