import http, { request as httpRequest } from 'http'
import https, { request as httpsRequest } from 'https'

export interface GetResult<T> {
    data?: T
    error?: Error
    options?: http.RequestOptions
}

export function get<T = any>(
    options: http.RequestOptions,
    useHttp?: boolean
): Promise<GetResult<T>> {
    const provider = useHttp ? http : https

    return new Promise((resolve) => {
        const req = provider.get(
            {
                ...options,
                headers: {
                    ...options?.headers,
                    'Content-Type': 'application/json',
                },
            },
            function (res) {
                const bodyChunks: Uint8Array[] = []
                res.on('data', (chunk) => {
                    bodyChunks.push(chunk)
                }).on('end', () => {
                    const body = Buffer.concat(bodyChunks).toString()
                    try {
                        if (res.statusCode === 200) {
                            resolve({ data: JSON.parse(body) })
                        } else {
                            resolve({
                                data: JSON.parse(body),
                                error: new Error(
                                    `HTTP response code: ${res.statusCode}`
                                ),
                            })
                        }
                    } catch (e) {
                        resolve({ error: e, options: options })
                    }
                })
            }
        )
        req.on('error', function (e) {
            resolve({ error: e, options: options })
        })
    })
}

export interface PostResult<T> {
    data?: T
    error?: Error
    cookie?: string[]
}

interface PostParams {
    hostname: string
    path: string
    port: number
    bodyObject: any
    http?: boolean
    cookie?: string
}

// https://nodejs.org/api/http.html#httprequesturl-options-callback
export function post<T = any>({
    hostname,
    path,
    port,
    bodyObject,
    http,
    cookie,
}: PostParams): Promise<PostResult<T>> {
    const provider = http ? httpRequest : httpsRequest
    const body = JSON.stringify(bodyObject)

    return new Promise((resolve) => {
        const req = provider(
            {
                hostname: hostname,
                port: port,
                path: path,
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Content-Length': Buffer.byteLength(body),
                    ...(cookie
                        ? {
                              cookie: cookie,
                          }
                        : {}),
                },
            },
            (res) => {
                const bodyChunks: string[] = []
                res.setEncoding('utf8')
                res.on('data', (chunk) => {
                    bodyChunks.push(chunk)
                })
                res.on('end', () => {
                    const body = bodyChunks.join()
                    try {
                        resolve({
                            data: JSON.parse(body),
                            cookie: res.headers['set-cookie'],
                        })
                    } catch (e) {
                        resolve({ error: e })
                    }
                })
            }
        )

        req.on('error', (e) => {
            resolve({ error: e })
        })

        req.end(body)
    })
}

export function parseCookie(responseCookie: string[]): {
    [key: string]: string
} {
    const cookieArray = responseCookie.join('; ').split('; ')
    const parsedCookie: { [key: string]: string } = cookieArray.reduce(
        (acc, cookieString) => {
            const [key, val] = cookieString.split('=')
            return {
                ...acc,
                [key]: val || true,
            }
        },
        {}
    )
    return parsedCookie
}
