import { Requests } from "../../requests";

const ZERO_DATE = "0001-01-01T00:00:00Z";

type BaseApiType = {
  createdAt: string;
  updatedAt: string;

  [key: string]: any;
};

export function hasKey(obj: Record<string, any>, key: string): obj is Required<BaseApiType> {
  return key in obj ? typeof obj[key] === "string" : false;
}

export function parseDate<T>(obj: T, keys: Array<keyof T> = []): T {
  const result = { ...obj };
  [...keys, "createdAt", "updatedAt"].forEach(key => {
    // @ts-expect-error - TS doesn't know that we're checking for the key above
    if (hasKey(result, key)) {
      const value = result[key] as string;

      if (value === undefined || value === "" || value.startsWith(ZERO_DATE)) {
        const dt = new Date();
        dt.setFullYear(1);

        result[key] = dt;
        return;
      }

      // Possible Formats
      // Date Only: YYYY-MM-DD
      // Timestamp: 0001-01-01T00:00:00Z

      // Parse timestamps with default date
      if (value.includes("T")) {
        result[key] = new Date(value);
        return;
      }

      // Parse dates with default time
      const split = value.split("-");

      if (split.length !== 3) {
        console.log(`Invalid date format: ${value}`);
        throw new Error(`Invalid date format: ${value}`);
      }

      const [year, month, day] = split;

      const dt = new Date();

      dt.setFullYear(parseInt(year, 10));
      dt.setMonth(parseInt(month, 10) - 1);
      dt.setDate(parseInt(day, 10));

      result[key] = dt;
    }
  });

  return result;
}

export class BaseAPI {
  http: Requests;
  attachmentToken: string;

  constructor(requests: Requests, attachmentToken = "") {
    this.http = requests;
    this.attachmentToken = attachmentToken;
  }

  // if a attachmentToken is present it will be added to URL as a query param
  // this is done with a simple appending of the query param to the URL. If your
  // URL already has a query param, this will not work.
  authURL(url: string): string {
    if (this.attachmentToken) {
      return `/api/v1${url}?access_token=${this.attachmentToken}`;
    }
    return url;
  }

  /**
   * dropFields will remove any fields that are specified in the fields array
   * additionally, it will remove the `createdAt` and `updatedAt` fields if they
   * are present. This is useful for when you want to send a subset of fields to
   * the server like when performing an update.
   */
  protected dropFields<T>(obj: T, keys: Array<keyof T> = []): T {
    const result = { ...obj };
    [...keys, "createdAt", "updatedAt"].forEach(key => {
      // @ts-ignore - we are checking for the key above
      if (hasKey(result, key)) {
        // @ts-ignore - we are guarding against this above
        delete result[key];
      }
    });
    return result;
  }
}