import { IncomingMessage } from 'http';
import { request, RequestOptions } from 'https';

import { ITokenProvider } from '../token/provider';

export type Method =
  | 'GET'
  | 'HEAD'
  | 'POST'
  | 'PUT'
  | 'DELETE'
  | 'CONNECT'
  | 'OPTIONS'
  | 'TRACE'
  | 'PATCH';

export class ApiProxy {
  constructor(private baseUrl: string, private tokenProvider: ITokenProvider) {}

  public async request<T>(
    method: Method,
    path: string,
    refreshToken = false
  ): Promise<T> {
    const url = this.baseUrl + path;
    const token = await (refreshToken
      ? this.tokenProvider.refresh()
      : this.tokenProvider.get());

    const options: RequestOptions = {
      method,
      headers: {
        authorization: `Bearer ${token}`,
      },
    };

    return new Promise<T>((resolve, reject) => {
      const callback = (response: IncomingMessage) => {
        let data = '';

        if (response.statusCode === 401) {
          return resolve(this.request(method, path, true));
        }

        if (response.statusCode !== 200) {
          return reject(response);
        }

        response.on('data', (chunk) => (data += chunk));
        response.on('end', () => {
          try {
            resolve(JSON.parse(data)['value'] as T);
          } catch (error) {
            reject(error);
          }
        });
      };

      request(url, options, callback).on('error', reject).end();
    });
  }
}