import https, { RequestOptions } from "https";
import { StatusError } from "./errors";

/**
 * The API URL
 */
const API_URL = "https://api.themoviedb.org/3";

/**
 * A request manager with atomic/persistent request options
 */
export default class ApiRequestManager
{
	/**
	 * The API Key
	 */
	private __apiKey: string;

	/**
	 * Store additional request options
	 */
	protected options: RequestOptions;

	/**
	 * Create a new API request manager
	 *
	 * @param options Additional request options
	 */
	public constructor(apiKey: string, options: RequestOptions = {}) {
		this.__apiKey = apiKey;
		this.options = options;
	}

	/**
	 * Perform a generic HTTPS request
	 *
	 * @param method    The HTTP method
	 * @param url       The URL to request
	 * @param apiKey An optional bearer token
	 * @param params    Optional parameters
	 * @param body      Optional body
	 */
	protected request<T>(method: string, url: string, params?: any, body?: string)
	{
		return new Promise<T>((resolve, reject) => {
			// Create request options
			let options = Object.assign({ method, headers: {} }, this.options);
			if (body) {
				options.headers["Content-Type"] = "application/json";
				options.headers["Content-Length"] = body.length;
			}

			// Add search parameters if necessary
			let requestUrl = new URL(url);
			requestUrl.searchParams.set("api_key", this.__apiKey);
			if (params) {
				Object.keys(params).forEach((key) => {
					if (params[key] !== undefined) {
						requestUrl.searchParams.set(key, params[key]);
					}
				});
			}

			// Create the request
			let request = https.request(<any>requestUrl, options, (res) => {
				let rawData: string = "";
				res.setEncoding("utf8");
				res.on("data", chunk => {rawData += chunk});
				res.on("error", reject);
				res.on("end", () => {
					let response: T = JSON.parse(rawData);
					if (res.statusCode == 200) {
						resolve(response)
					} else {
						reject(new StatusError(response, <number>res.statusCode));
					}
				});
			})
			.on("error", reject)
			.on("timeout", () => reject("timeout"));
			if (body) {
				request.write(body);
			}
			request.end();
		});
	}

	/**
	 * Perform a generic GET request
	 */
	public async get<T = any>(path: string, params?: any) {
		return await this.request<T>("GET", `${API_URL}${path}`, params);
	}

	/**
	 * Perform a generic POST request
	 */
	public async post<T = any>(path: string, params?: any, body?: any) {
		if (body !== undefined) {
			body = JSON.stringify(body);
		}
		return await this.request<T>("POST", `${API_URL}${path}`, params, body);
	}
}
