import https, { RequestOptions } from "https";

/**
 * The URL to the Plex server
 */
const API_URL = <string>process.env["PLEX_URL"];

/**
 * A status error is used to indicate responses with non-200 status codes
 */
export class StatusError<T = any> extends Error
{
	/**
	 * The resulting body of a response
	 */
	public readonly response: T;

	/**
	 * The resulting status code of a response
	 */
	public readonly statusCode?: number;

	/**
	 * Create a new error indicating non-200 status
	 */
	public constructor(response: T, statusCode: number) {
		super();
		Object.setPrototypeOf(this, StatusError.prototype);
		this.response = response;
		this.statusCode = statusCode;
	}
}

/**
 * A request manager with atomic/persistent request options
 */
export default class ApiRequestManager
{
	/**
	 * The URL to the Plex server
	 */
	protected URL: string;

	/**
	 * The authorization token
	 */
	protected TOKEN: string;

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

	/**
	 * Create a new API request manager
	 *
	 * @param options Additional request options
	 */
	public constructor(url: string, plexToken: string, options: RequestOptions = {}) {
		this.URL = url;
		this.TOKEN = plexToken;
		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(method: string, url: string, params?: any)
	{
		return new Promise<string>((resolve, reject) => {
			// Create request options
			let options = Object.assign({ method, headers: {} }, this.options);

			// Add search parameters if necessary
			let requestUrl = new URL(url);
			requestUrl.searchParams.set("X-Plex-Token", this.TOKEN);
			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", async () => {
					rawData;
					if (res.statusCode == 200) {
						resolve(rawData)
					} else {
						reject(new StatusError(rawData, <number>res.statusCode));
					}
				});
			})
			.on("error", reject)
			.on("timeout", () => reject("timeout"));
			request.end();
		});
	}

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