import type {
  AxiosBasicCredentials,
  AxiosRequestConfig,
  Method,
  RawAxiosRequestHeaders,
} from "axios";
import axios from "axios";
import { APIError } from "./api_error";

interface Params {
  endpoint: string;
  method?: Method;
  data?: unknown;
  headers?: RawAxiosRequestHeaders;
  config?: AxiosRequestConfig;
}

export class API {
  private static readonly axios = axios.create({
    timeout: 15e3,
    baseURL: "/api",
  });

  private static readonly creds_key = "advent22/credentials";

  public static set creds(value: AxiosBasicCredentials | null) {
    if (value === null) {
      localStorage.removeItem(this.creds_key);
    } else {
      localStorage.setItem(this.creds_key, JSON.stringify(value));
    }
  }

  public static get creds(): AxiosBasicCredentials {
    const stored_auth = JSON.parse(localStorage.getItem(this.creds_key) ?? "null");
    if (
      stored_auth !== null &&
      Object.hasOwn(stored_auth, "username") &&
      Object.hasOwn(stored_auth, "password")
    ) {
      return stored_auth;
    }

    return { username: "", password: "" };
  }

  private static get_axios_config({
    endpoint,
    method = "GET",
    data,
    headers = {},
    config = {},
  }: Params): AxiosRequestConfig {
    return {
      url: endpoint,
      method: method,
      data: data,
      auth: this.creds,
      headers: headers,
      ...config,
    };
  }

  public static async request<T = string>(p: Params): Promise<T>;
  public static async request<T = string>(p: string): Promise<T>;
  public static async request<T = string>(p: Params | string): Promise<T> {
    if (typeof p === "string") p = { endpoint: p };

    try {
      const response = await this.axios.request<T>(this.get_axios_config(p));
      return response.data;
    } catch (reason) {
      console.error(`Failed to query ${p.endpoint}: ${reason}`);
      throw new APIError(reason, p.endpoint);
    }
  }
}
