import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Observable, throwError } from "rxjs";
import { MagicMirrorPackage } from "src/app/interfaces/interfaces";
import { retry, catchError } from "rxjs/operators";
import { URLS } from "src/app/utils/urls";

const httpOptions = (httpHeaders: object = {}) =>
  new HttpHeaders({
    "Content-Type": "application/json",
    ...httpHeaders,
  });

@Injectable({
  providedIn: "root",
})
export class RestApiService {
  constructor(private http: HttpClient) {}

  private route(path: string): string {
    return `http://${window.location.hostname}:7890/api${path}`;
  }

  public retrieve(path: string): Promise<any> {
    return this.http
      .get<any>(this.route(path), {
        headers: httpOptions(),
      })
      .pipe(retry(1), catchError(this.handleError))
      .toPromise();
  }

  public getFile(url: string): Promise<any> {
    return this.http
      .get(this.route(url), {
        headers: httpOptions(),
        responseType: "text",
      })
      .pipe(retry(1), catchError(this.handleError))
      .toPromise();
  }

  private postWithSelectedPackages(url: string, selectedPackages: MagicMirrorPackage[]): Promise<any> {
    return this.http
      .post(
        this.route(url),
        {
          "selected-packages": selectedPackages,
        },
        {
          headers: httpOptions({
            "Content-Type": "application/x-www-form-urlencoded",
          }),
          responseType: "text",
          reportProgress: true,
        },
      )
      .pipe(retry(1), catchError(this.handleError))
      .toPromise();
  }

  public packagesInstall(selectedPackages: MagicMirrorPackage[]): Promise<any> {
    return this.postWithSelectedPackages(URLS.POST.PACKAGES.INSTALL, selectedPackages);
  }

  public packagesUpgrade(selectedPackages: MagicMirrorPackage[]): Promise<any> {
    return this.postWithSelectedPackages(URLS.POST.PACKAGES.UPGRADE, selectedPackages);
  }

  public packagesRemove(selectedPackages: MagicMirrorPackage[]): Promise<any> {
    return this.postWithSelectedPackages(URLS.POST.PACKAGES.REMOVE, selectedPackages);
  }

  public getLogFiles(): Promise<any> {
    return this.http
      .get(this.route(URLS.GET.MMPM.DOWNLOAD_LOGS), {
        headers: httpOptions({
          "Content-Type": "application/zip",
        }),
        reportProgress: true,
        responseType: "arraybuffer",
      })
      .pipe(retry(1), catchError(this.handleError))
      .toPromise();
  }

  public updateMagicMirrorConfig(url: string, code: string): Observable<Response> {
    return this.http
      .post<any>(
        this.route(url),
        {
          code,
        },
        {
          headers: httpOptions({
            "Content-Type": "application/x-www-form-urlencoded",
          }),
        },
      )
      .pipe(retry(1), catchError(this.handleError));
  }

  public addExternalPackage(externalSource: MagicMirrorPackage): Promise<any> {
    return this.http
      .post<any>(
        this.route(URLS.POST.EXTERNAL_PACKAGES.ADD),
        {
          "external-package": externalSource,
        },
        {
          headers: httpOptions({
            "Content-Type": "application/x-www-form-urlencoded",
          }),
        },
      )
      .pipe(retry(1), catchError(this.handleError))
      .toPromise();
  }

  public rotateRaspberryPiScreen(degrees: number): Promise<boolean> {
    return this.http
      .post<boolean>(
        this.route(URLS.POST.RASPBERRYPI.ROTATE_SCREEN),
        {
          degrees: String(degrees),
        },
        {
          headers: httpOptions({
            "Content-Type": "application/x-www-form-urlencoded",
          }),
        },
      )
      .pipe(retry(1), catchError(this.handleError))
      .toPromise();
  }

  public removeExternalPackage(externalSources: MagicMirrorPackage[]): Promise<any> {
    return this.http
      .request("DELETE", this.route(URLS.DELETE.EXTERNAL_PACKAGES.REMOVE), {
        body: {
          "external-packages": externalSources,
        },
        headers: httpOptions({
          "Content-Type": "text/plain",
        }),
      })
      .pipe(retry(1), catchError(this.handleError))
      .toPromise();
  }

  public upgradeMagicMirror(): Promise<any> {
    return this.retrieve(URLS.GET.MAGICMIRROR.UPGRADE);
  }

  public handleError(error: any): Promise<any> {
    const errorMessage = error.error instanceof ErrorEvent ? error.error.message : `Error Code: ${error.status}\nMessage: ${error.message}`;

    window.alert(errorMessage);
    return throwError(errorMessage).toPromise();
  }
}
