import type { Operator, SubqueryBuilder } from '@stacksjs/orm';
import type { RawBuilder } from '@stacksjs/database';

export declare interface QueryBuilder<T, M, R = any> {
  where: <V = string>(column: keyof T, ...args: [V] | [Operator, V]) => M

  find: (id: number) => Promise<M | undefined>

  get: () => Promise<M[]>

  orderBy: (column: keyof T, order: 'asc' | 'desc') => M

  join: (table: string, firstCol: string, secondCol: string) => M

  findOrFail: (id: number) => Promise<M>

  whereIn: <V = number>(column: keyof T, values: V[]) => M

  whereNotIn: <V = number>(column: keyof T, values: V[]) => M

  whereNull: (column: keyof T) => M

  whereNotNull: (column: keyof T) => M

  whereBetween: <V = number>(column: keyof T, range: [V, V]) => M

  whereRaw: (sqlStatement: string) => M

  whereLike: (column: keyof T, value: string) => M

  whereExists: (callback: (qb: any) => any) => M

  when: (condition: boolean, callback: (query: M) => M) => M

  select: (params: (keyof R)[] | RawBuilder<string> | string) => M

  first: () => Promise<M | undefined>

  firstOrFail: () => Promise<M>

  take: (count: number) => M

  skip: (count: number) => M

  count: () => Promise<number>

  exists: () => Promise<boolean>

  with: (relations: string[]) => M

  max: (field: keyof T) => Promise<number>

  min: (field: keyof T) => Promise<number>

  chunk: (size: number, callback: (models: M[]) => Promise<void>) => Promise<void>

  avg: (field: keyof T) => Promise<number>

  sum: (field: keyof T) => Promise<number>

  orderByDesc: (column: keyof T) => M

  orderByAsc: (column: keyof T) => M

  pluck: <K extends keyof M>(field: K) => Promise<M[K][]>

  groupBy: (column: keyof T) => M

  having: <V = string>(column: keyof T, operator: Operator, value: V) => M

  orWhere: (...conditions: [string, any][]) => M

  has: (relation: string) => M

  whereHas: (relation: string, callback: (query: SubqueryBuilder<keyof T>) => void) => M

  doesntHave: (relation: string) => M

  inRandomOrder: () => M

  whereColumn: (first: keyof T, operator: Operator, second: keyof T) => M

  distinct: (column: keyof R) => M

  latest: () => Promise<M | undefined>

  oldest: () => Promise<M | undefined>

  last: () => Promise<M | undefined>

  findMany: (ids: number[]) => Promise<M[]>

  whereRef: (column: keyof T, ...args: string[]) => M
}