export type getOptions = {
  meta?: boolean
  reverse?: boolean
  limit?: number
  start?: string
  label?: labels
}

export type setOptions = {
  ttl?: number | string | null
  overwrite?: boolean
  label1?: string
  label2?: string
  label3?: string
  label4?: string
  label5?: string
  meta?: boolean
}

export type setBatchOptions =
  | {
      meta?: boolean // for returning updated item
    }
  | boolean

export type addOptions = {
  overwrite?: boolean
  meta?: boolean
}

export type Optional<T> = T | undefined

type WithMeta<T> = T & { meta?: any }

type KeyValue<T> = {
  key: string
  value: WithMeta<T>
}

export type GetResponse<T> = {
  items: KeyValue<T>[]
  lastKey?: string
  next?: () => Promise<GetResponse<T>>
}

type SetResponse<T> = KeyValue<T> | T

type AddResponse<T> = KeyValue<T> | T | undefined

export type labels = 'label1' | 'label2' | 'label3' | 'label4' | 'label5'

// .get types
type Whitespace = '\n' | ' '
type Trim<T> = T extends `${Whitespace}${infer U}` ? Trim<U> : T extends `${infer U}${Whitespace}` ? Trim<U> : T
type WildcardQuery = `${string}:${string | ''}*`
type ComparisonQuery = `${string}:${string | ''}${Trim<'<'> | Trim<'>'> | Trim<'<='> | Trim<'>='>}${string}`
type RangeQuery = `${string}:${string}${Trim<'|'>}${string}`
export type Query = WildcardQuery | ComparisonQuery | RangeQuery

type DataEventHandler = (event: { previous?: any; item: any }) => void

export declare interface Data {
  get<T>(keys: Query, options?: getOptions | boolean): Promise<GetResponse<T>>
  get<T>(keys: string[], options?: getOptions | boolean): Promise<GetResponse<T>>
  get<T>(keys: string, options?: getOptions | boolean): Promise<Optional<T>>
  getByLabel<T>(label: labels, keys: Query, options?: getOptions | boolean): Promise<GetResponse<T>>
  getByLabel<T>(label: labels, keys: string[], options?: getOptions | boolean): Promise<GetResponse<T>>
  getByLabel<T>(label: labels, keys: string, options?: getOptions | boolean): Promise<Optional<T>>
  getByLabel<T>(label: labels, keys: string | string[], options?: getOptions | boolean): Promise<GetResponse<T>>
  remove: (keys: string | string[]) => Promise<boolean>
  set<T>(keys: string, value: any, opts?: setOptions | boolean): Promise<SetResponse<T>>
  set<T>(keys: KeyValue<T>[], opts?: setBatchOptions | boolean): Promise<SetResponse<T>>
  add<T>(key: string, value: number, opts?: addOptions | boolean): Promise<AddResponse<T>>
  add<T>(key: string, attribute: string, value: number, opts?: addOptions | boolean): Promise<AddResponse<T>>
  seed: (
    itemsOrPath: string | any[],
    overwrite: boolean
  ) => Promise<{
    items: number
  }>
  on: (name: string | string[], handler: DataEventHandler) => void
}

declare const data: Data
export default data
