import { NetworkRequest } from '@networking/NetworkRequest'
import { PublishResponse } from './PublishResponse'

export interface NetworkClientInterface {

    // Performs the network request. It takes a network request
    // and returns a Promise. `T` specifies the return type and is used
    // by the network client to decode the network payload.
    run<T>(request: NetworkRequest): Promise<T>
    publish(request: NetworkRequest): Promise<PublishResponse>
}

/*
 * Network Client used to perform all the network requests
 * in the plugin.
 *
 * The network client takes a closure which returns the application
 * token. This allows the app token to be resolved only when it's needed,
 */
export class NetworkClient implements NetworkClientInterface {

    // Properties

    private appToken: () => string

    // Life cycle

    constructor(
        appToken: () => string,
    ) {
        this.appToken = appToken
    }

    private requestHeaders(): HeadersInit {
        return {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Accept': 'application/json',
            'Authorization': 'Bearer ' + this.appToken()
        }
    }

    // Public

    public async run<T>(request: NetworkRequest): Promise<T> {
        const url = request.url + '?' + request.parameters

        const response = await fetch(url, {
            method: request.method,
            headers: this.requestHeaders(),
            body: request.body
        })

        if (!response.ok) {
            throw new Error(
                `Network error: ${response.status}`
            )
        }

        return await response.json() as T
    }

    public async publish(request: NetworkRequest): Promise<PublishResponse> {
        const response: Response = await fetch(request.url, {
            method: request.method,
            headers: this.requestHeaders(),
            body: request.body
        })

        if (!response.ok) {
            throw new Error(
                `Network error: ${response.status}`
            )
        }

        const postUrl: string | null = response.headers.get("Location") || response.headers.get("location")

        if (!postUrl) {
            throw new Error(
                "Response is missing url of new post."
            )
        }

        return {
            url: postUrl
        }
    }
}