import { APIClient } from "./ApiClient";

export enum CreateAccountResult {
  TooManyRequests,
  BadInputData,
  MailAlreadyExists,
  Success,
  Error,
}

export enum PasswordLoginResult {
  TooManyRequests,
  InvalidCredentials,
  Success,
  Error,
}

export interface CheckResetTokenResponse {
  name: string;
}

const TokenStateKey = "auth-token";

export class AuthApi {
  /**
   * Check out whether user is signed in or not
   */
  static get SignedIn(): boolean {
    return localStorage.getItem(TokenStateKey) !== null;
  }

  /**
   * Get user auth token
   */
  static get AuthToken(): string {
    if (!this.SignedIn) throw new Error("User is not authenticated!");
    return localStorage.getItem(TokenStateKey)!;
  }

  /**
   * Create a new account
   */
  static async CreateAccount(
    name: string,
    mail: string
  ): Promise<CreateAccountResult> {
    const res = await APIClient.exec({
      uri: "/auth/create_account",
      method: "POST",
      allowFail: true,
      jsonData: {
        name: name,
        email: mail,
      },
    });

    switch (res.status) {
      case 429:
        return CreateAccountResult.TooManyRequests;
      case 400:
        return CreateAccountResult.BadInputData;
      case 409:
        return CreateAccountResult.MailAlreadyExists;
      case 200:
      case 201:
        return CreateAccountResult.Success;
      default:
        return CreateAccountResult.Error;
    }
  }

  /**
   * Authenticate using an email and a password
   *
   * @param mail The email address to use
   * @param password The password to use
   */
  static async LoginWithPassword(
    mail: string,
    password: string
  ): Promise<PasswordLoginResult> {
    const res = await APIClient.exec({
      uri: "/auth/password_login",
      method: "POST",
      allowFail: true,
      jsonData: {
        mail: mail,
        password: password,
      },
    });

    switch (res.status) {
      case 429:
        return PasswordLoginResult.TooManyRequests;
      case 401:
        return PasswordLoginResult.InvalidCredentials;
      case 200:
      case 201:
        localStorage.setItem(TokenStateKey, res.data.token);
        return PasswordLoginResult.Success;
      default:
        return PasswordLoginResult.Error;
    }
  }

  /**
   * Start OpenID login
   *
   * @param id The ID of the OIDC provider to use
   */
  static async StartOpenIDLogin(id: string): Promise<{ url: string }> {
    return (
      await APIClient.exec({
        uri: "/auth/start_openid_login",
        method: "POST",
        jsonData: { provider: id },
      })
    ).data;
  }

  /**
   * Finish OpenID login
   */
  static async FinishOpenIDLogin(code: string, state: string): Promise<void> {
    const res: { user_id: number; token: string } = (
      await APIClient.exec({
        uri: "/auth/finish_openid_login",
        method: "POST",
        jsonData: { code: code, state: state },
      })
    ).data;

    localStorage.setItem(TokenStateKey, res.token);
  }

  /**
   * Sign out
   */
  static async SignOut(): Promise<void> {
    await APIClient.exec({
      uri: "/auth/logout",
      method: "GET",
    });

    this.RemoveAuthToken();
  }

  /**
   * Remove auth token from storage
   */
  static RemoveAuthToken() {
    localStorage.removeItem(TokenStateKey);
  }

  /**
   * Request to reset password
   */
  static async RequestResetPassword(mail: string): Promise<void> {
    await APIClient.exec({
      uri: "/auth/request_reset_password",
      method: "POST",
      jsonData: { mail: mail },
    });
  }

  /**
   * Check reset password token
   */
  static async CheckResetPasswordToken(
    token: string
  ): Promise<CheckResetTokenResponse> {
    return (
      await APIClient.exec({
        uri: "/auth/check_reset_password_token",
        method: "POST",
        jsonData: { token: token },
      })
    ).data;
  }

  /**
   * Reset password
   */
  static async ResetPassword(
    token: string,
    newPassword: string
  ): Promise<void> {
    await APIClient.exec({
      uri: "/auth/reset_password",
      method: "POST",
      jsonData: { token: token, password: newPassword },
    });
  }
}
