export const headerRateScope = "X-RateLimit-Scope";

export const headerAPILimit = "X-RateLimit-Limit";
export const headerAPIRateRemaining = "X-RateLimit-Remaining";
export const headerAPIRateReset = "X-RateLimit-Reset";

export const headerQueryLimit = "X-QueryLimit-Limit";
export const headerQueryRemaining = "X-QueryLimit-Remaining";
export const headerQueryReset = "X-QueryLimit-Reset";

export const headerIngestLimit = "X-IngestLimit-Limit";
export const headerIngestRemaining = "X-IngestLimit-Remaining";
export const headerIngestReset = "X-IngestLimit-Reset";

export enum LimitScope {
  unknown = "unknown",
  user = "user",
  organization = "organization",
  anonymous = "anonymous",
}
export enum LimitType {
  api = "api",
  query = "query",
  ingest = "ingest",
}

export class Limit {
  constructor(
    public scope: LimitScope = LimitScope.unknown,
    public type: LimitType = LimitType.api,
    public value: number = 0,
    public remaining: number = -1,
    public reset: Date = new Date(),
  ) {}
}

// parse limit headers from axios response and return a limit object
export function parseLimitFromResponse(response: Response): Limit {
  let limit: Limit;

  if (response.url?.endsWith("/ingest")) {
    limit = parseLimitFromHeaders(response, "", headerIngestLimit, headerIngestRemaining, headerIngestReset);
    limit.type = LimitType.ingest;
  } else if (response.url?.endsWith("/query") || response.url?.endsWith("/_apl")) {
    limit = parseLimitFromHeaders(response, "", headerQueryLimit, headerQueryRemaining, headerQueryReset);
    limit.type = LimitType.query;
  } else {
    limit = parseLimitFromHeaders(
      response,
      headerRateScope,
      headerAPILimit,
      headerAPIRateRemaining,
      headerAPIRateReset,
    );
    limit.type = LimitType.api;
  }

  return limit;
}

export const limitKey = (type: LimitType, scope: LimitScope): string => `${type}:${scope}`;

// parseLimitFromHeaders parses the named headers from a `*http.Response`.
function parseLimitFromHeaders(
  response: Response,
  headerScope: string,
  headerLimit: string,
  headerRemaining: string,
  headerReset: string,
): Limit {
  const limit: Limit = new Limit();

  const scope: string = response.headers.get(headerScope.toLowerCase()) || LimitScope.unknown;
  limit.scope = LimitScope[scope as keyof typeof LimitScope];

  const limitValue = response.headers.get(headerLimit.toLowerCase()) || "";
  const limitValueNumber = parseInt(limitValue, 10);
  if (!isNaN(limitValueNumber)) {
    limit.value = limitValueNumber;
  }

  const remainingValue = response.headers.get(headerRemaining.toLowerCase()) || "";
  const remainingValueNumber = parseInt(remainingValue, 10);
  if (!isNaN(remainingValueNumber)) {
    limit.remaining = remainingValueNumber;
  }

  const resetValue = response.headers.get(headerReset.toLowerCase()) || "";
  const resetValueInt = parseInt(resetValue, 10);
  if (!isNaN(resetValueInt)) {
    limit.reset = new Date(resetValueInt * 1000);
  }

  return limit;
}
