import { request as httpRequest } from 'http';
import { request as httpsRequest } from 'https';
import { gzip as gzipCallback, createGunzip } from 'zlib';
import { promisify } from 'util';
import { URL } from 'url';
import { z } from 'zod';
import sql from 'mssql';
const gzip = promisify(gzipCallback);
export async function sendPostRequest(url, payload, useCompression, headers, streamCallback) {
    return new Promise(async (resolve, reject) => {
        try {
            const { protocol, hostname, port, pathname } = new URL(url);
            let data;
            if (useCompression) {
                try {
                    data = await gzip(Buffer.from(JSON.stringify(payload)));
                    headers = headers || {};
                    headers['Content-Encoding'] = 'gzip';
                }
                catch (error) {
                    const err = z.object({ message: z.string() }).safeParse(error);
                    console.error(`Error in sendPostRequest while compressing data: ${err.success ? err.data.message : error}`);
                    return reject(error);
                }
            }
            else {
                data = Buffer.from(JSON.stringify(payload));
            }
            const options = {
                hostname,
                port: port || (protocol === 'https:' ? 443 : 80),
                path: pathname,
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    ...headers
                }
            };
            const request = protocol === 'https:' ? httpsRequest : httpRequest;
            const jsonObjects = [];
            let buffer = '';
            const req = request(options, (res) => {
                const gunzip = createGunzip();
                const stream = res.headers['content-encoding'] === 'gzip' ? res.pipe(gunzip) : res;
                stream.on('data', (chunk) => {
                    buffer += chunk;
                    let boundary;
                    while ((boundary = buffer.indexOf('\n')) !== -1) {
                        const jsonString = buffer.substring(0, boundary);
                        buffer = buffer.substring(boundary + 1);
                        try {
                            const jsonObject = JSON.parse(jsonString);
                            jsonObjects.push(jsonObject);
                            streamCallback?.(jsonObject);
                        }
                        catch (e) {
                            const err = z.object({ message: z.string() }).safeParse(e);
                            console.warn(`Error in postRequest().stream(data) while parsing JSON object: ${err.success ? err.data.message : e}`);
                        }
                    }
                });
                stream.on('end', () => {
                    if (buffer.trim()) {
                        try {
                            const jsonObject = JSON.parse(buffer.trim());
                            jsonObjects.push(jsonObject);
                            streamCallback?.(jsonObject);
                        }
                        catch (e) {
                            const err = z.object({ message: z.string() }).safeParse(e);
                            console.warn(`Error in postRequest().stream(end) while parsing JSON object: ${err.success ? err.data.message : e}`);
                        }
                    }
                    resolve(jsonObjects);
                });
            });
            req.on('error', (e) => {
                const err = z.object({ message: z.string() }).safeParse(e);
                console.error(`Error in sendPostRequest().req.on(error): ${err.success ? err.data.message : e}`);
                reject(e);
            });
            req.write(data);
            req.end();
        }
        catch (e) {
            const err = z.object({ message: z.string() }).safeParse(e);
            console.error(`Error in sendPostRequest: ${err.success ? err.data.message : e}`);
            reject(e);
        }
    });
}
export function GetReadOnlyDataSource(dataSources, options) {
    const readOnlyDataSource = dataSources.find((ds) => ds.type === 'Read-Only');
    if (readOnlyDataSource) {
        return extendConnectionPoolWithQuery(readOnlyDataSource.dataSource);
    }
    else if (!options || options.allowFallbackToReadWrite) {
        const readWriteDataSource = dataSources.find((ds) => ds.type === 'Read-Write');
        if (readWriteDataSource) {
            return extendConnectionPoolWithQuery(readWriteDataSource.dataSource);
        }
    }
    throw new Error('No suitable data source found');
}
export function GetReadOnlyProvider(providers, options) {
    if (!providers || providers.length === 0)
        return null;
    const readOnlyProvider = providers.find((p) => p.type === 'Read-Only');
    if (readOnlyProvider) {
        return readOnlyProvider.provider;
    }
    else if (options?.allowFallbackToReadWrite) {
        return providers[0].provider;
    }
    else {
        return null;
    }
}
export function GetReadWriteProvider(providers, options) {
    if (!providers || providers.length === 0)
        return null;
    const readWriteProvider = providers.find((p) => p.type === 'Read-Write');
    if (readWriteProvider) {
        return readWriteProvider.provider;
    }
    else if (options?.allowFallbackToReadOnly) {
        return GetReadOnlyProvider(providers, { allowFallbackToReadWrite: false });
    }
    else {
        return null;
    }
}
export function GetReadWriteDataSource(dataSources) {
    const readWriteDataSource = dataSources.find((ds) => ds.type === 'Read-Write');
    if (readWriteDataSource) {
        return extendConnectionPoolWithQuery(readWriteDataSource.dataSource);
    }
    throw new Error('No suitable read-write data source found');
}
export function extendConnectionPoolWithQuery(pool) {
    const extendedPool = pool;
    extendedPool.query = async (sqlQuery, parameters) => {
        const request = new sql.Request(pool);
        if (parameters) {
            if (Array.isArray(parameters)) {
                parameters.forEach((value, index) => {
                    request.input(`p${index}`, value);
                });
                let paramIndex = 0;
                sqlQuery = sqlQuery.replace(/\?/g, () => `@p${paramIndex++}`);
            }
        }
        const result = await request.query(sqlQuery);
        return result.recordset || [];
    };
    return extendedPool;
}
//# sourceMappingURL=util.js.map