import * as _nuxt_schema from '@nuxt/schema';
import { PublicRuntimeConfig, RuntimeConfig } from '@nuxt/schema';
import { FetchError, FetchRequest as FetchRequest$1, FetchOptions, ResponseType } from 'ofetch';
import { AsyncDataOptions as AsyncDataOptions$1 } from 'nuxt/app';
import { Ref, WatchSource, ComputedRef } from 'vue';
import { AsyncDataOptions, AsyncData } from '#app';
import { Overrides as Overrides$1 } from '#surrealdb/types/index';

/**
 * The following is a custom implementation of Nuxt's useFetch composable.
 * It is meant to be used in a Nuxt project when connecting to third-party APIs,
 * while using a custom $fetch loaded as a Nuxt plugin.
 *
 * Source:
 * https://gist.github.com/sandros94/9105defd5f02d2614372da6d53023822
 */

type MultiWatchSources = (WatchSource<unknown> | object)[];
type PickFrom<T, K extends Array<string>> = T extends Array<any> ? T : T extends Record<string, any> ? keyof T extends K[number] ? T : K[number] extends never ? T : Pick<T, K[number]> : T;
type KeysOf$1<T> = Array<T extends T ? keyof T extends string ? keyof T : never : never>;
type FetchRequest = Exclude<FetchRequest$1, string> | (string & {});
type _AvailableMethods = 'get' | 'post' | 'patch' | 'delete' | 'search';
type AvailableMethods = _AvailableMethods | Uppercase<_AvailableMethods>;
type ComputedOptions<T extends Record<string, any>> = {
    [K in keyof T]: T[K] extends Function ? T[K] : ComputedOptions<T[K]> | Ref<T[K]> | T[K];
};
interface CustomFetchOptions<M extends AvailableMethods = AvailableMethods> extends FetchOptions, Overrides$1 {
    method?: M;
}
interface UseSurrealFetchOptions<ResT, DataT = ResT, DefaultT = undefined, PickKeys extends KeysOf$1<DataT> = KeysOf$1<DataT>, M extends AvailableMethods = AvailableMethods> extends Omit<AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>, 'watch'>, ComputedOptions<CustomFetchOptions<M>> {
    key?: string;
    $fetch?: typeof globalThis.$fetch;
    watch?: MultiWatchSources | false;
}
/**
 * Fetch data from an API endpoint with an SSR-friendly composable.
 * @param request The URL to fetch
 * @param opts extends $surrealFetch options and useAsyncData options
 */
declare function useSurrealFetch<ResT = void, DataT = ResT, DefaultT = undefined, PickKeys extends KeysOf$1<DataT> = KeysOf$1<DataT>, ErrorT = FetchError, ReqT extends FetchRequest = FetchRequest, Method extends AvailableMethods = AvailableMethods>(request: Ref<ReqT> | ReqT | (() => ReqT), opts?: UseSurrealFetchOptions<ResT, DataT, DefaultT, PickKeys, Method>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, ErrorT | undefined>;
declare function useSurrealFetch<ResT = void, DataT = ResT, DefaultT = DataT, PickKeys extends KeysOf$1<DataT> = KeysOf$1<DataT>, ErrorT = FetchError, ReqT extends FetchRequest = FetchRequest, Method extends AvailableMethods = AvailableMethods>(request: Ref<ReqT> | ReqT | (() => ReqT), opts?: UseSurrealFetchOptions<ResT, DataT, DefaultT, PickKeys, Method>): AsyncData<PickFrom<DataT, PickKeys> | DefaultT, ErrorT | undefined>;

interface User {}

interface UserSession {
  user?: User
}

interface UserSessionRequired extends UserSession {
  user: User
}

interface UserSessionComposable {
  loggedIn: ComputedRef<boolean>
  user: ComputedRef<User | null>
  session: Ref<UserSession>
  fetch: () => Promise<void>
  clear: () => Promise<void>
}

/* Database Overrides */

type DatabasePresetKeys = keyof PublicRuntimeConfig['surrealdb']['databases']
type DatabasePresetServerKeys =
  keyof PublicRuntimeConfig['surrealdb']['databases']
  & keyof RuntimeConfig['surrealdb']['databases']

type AuthToken = string | {
  user: string
  pass: string
}

interface Overrides {
  database?: DatabasePresetKeys | DatabasePreset
  token?: AuthToken | boolean
}

interface ServerOverrides {
  database?: DatabasePresetServerKeys | DatabasePreset
  token?: AuthToken | boolean
}

interface DatabasePreset {
  host?: string
  ws?: string
  NS?: string
  DB?: string
  SC?: string
  AC?: string
  auth?: AuthToken
}

/* useAsyncData and useFetch custom options */

type SurrealAsyncDataOptions<T, R = T> = AsyncDataOptions$1<T, R> & Overrides & {
  key?: string
}
type SurrealFetchOptions<
  T extends ResponseType = 'json',
> = Omit<FetchOptions<T>, 'method'> & {
  method?: Uppercase<SurrealMethods> | SurrealMethods
}
type UseSurrealRpcOptions<
  ResT,
  DataT = ResT,
  DefaultT = undefined,
  PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
> = Omit<UseSurrealFetchOptions<ResT, DataT, DefaultT, PickKeys>, 'method' | 'body' | 'onResponse'>

/* Utils */

type JSONPatchPath<T> = T extends object ? {
  [K in keyof T]-?: K extends string ? `/${K}` | `/${K}${Path<T[K]>}` : never
}[keyof T] : ''

type JSONPatchValue<T, P extends string> = P extends `${infer K}/${infer Rest}`
  ? K extends keyof T
    ? Rest extends JSONPatchPath<T[K]>
      ? JSONPatchValue<T[K], Rest>
      : T[K]
    : never
  : P extends keyof T
    ? T[P]
    : never

type JSONPatch<T> = {
  op: 'add' | 'remove' | 'replace' | 'copy' | 'move' | 'test'
  from?: JSONPatchPath<T>
  path: JSONPatchPath<T>
  value?: JSONPatchValue<T, JSONPatchPath<T>>
}

type SurrealMethods = 'get' | 'post' | 'put' | 'patch' | 'delete'

/* SurrealDB RPC Methods and Params types */

type CreateParams<T> = [
  thing: string,
  data?: Partial<T>,
]

type InsertParams<T> = [
  thing: string,
  data?: Partial<T>,
]

type MergeParams<T> = [
  thing: string,
  data: Partial<T>,
]

type PatchParams<T> = [
  thing: string,
  patches: JSONPatch<T>[],
  diff?: boolean,
]

type QueryParams = [
  sql: string,
  vars?: Record<string, any>,
]

// TODO: Check if it is possible to not use Record<string, string | undefined>
type SignInParams = [Record<string, string | undefined>]

type SignUpParams = [{
  NS: string
  DB: string
  [key: string]: string
}]

type UpdateParams<T> = [
  thing: string,
  data?: Partial<T>,
]

interface RpcMethods<T> {
  authenticate: [string]
  create: CreateParams<T>
  delete: [string]
  info: never
  insert: InsertParams<T>
  invalidate: never
  merge: MergeParams<T>
  patch: PatchParams<T>
  query: QueryParams
  select: [string]
  signin: SignInParams
  signup: SignUpParams
  update: UpdateParams<T>
}

interface RpcMethodsWS<T> extends RpcMethods<T> {
  kill: [string]
  let: [
    name: string,
    value: any,
  ]
  live: [
    table: string,
    diff?: Partial<T>,
  ]
  unset: [string]
  use: [
    NS?: string,
    DB?: string,
  ]
}

type RpcParams<T, M extends keyof RpcMethods<T>> = RpcMethods<T>[M]
type RpcParamsWS<T, M extends keyof RpcMethodsWS<T>> = RpcMethodsWS<T>[M]

/* SurrealDB RPC Request and Response types */

interface RpcRequest<
  T = any,
  M extends keyof RpcMethods<T> = keyof RpcMethods<T>,
  P extends RpcParams<T, M> = RpcParams<T, M>,
> {
  method: M
  params?: P
}

interface RpcRequestWS<
  T = any,
  M extends keyof RpcMethodsWS<T> = keyof RpcMethodsWS<T>,
  P extends RpcParamsWS<T, M> = RpcParamsWS<T, M>,
> {
  method: M
  params?: P
}

interface RpcResponseOk<R> {
  result: R
  error: never
}
interface RpcResponseOkWS<R> {
  id: number
  result: R
  error: never
}

interface RpcResponseError {
  result: never
  error: {
    code: number
    message: string
  }
}
interface RpcResponseErrorWS {
  id: number
  result: never
  error: {
    code: number
    message: string
  }
}

type RpcResponse<R> = RpcResponseOk<R> | RpcResponseError
type RpcResponseWS<R> = RpcResponseOkWS<R> | RpcResponseErrorWS

/* SurrealDB HTTP Response types */

interface HttpResponseOk<R> {
  result: R
  status: 'OK'
  time: string
}

interface HttpResponseError {
  result: string
  status: 'ERR'
  time: string
}

type HttpResponse<R> = Array<(HttpResponseOk<R> | HttpResponseError)>
type HttpRes<R> = HttpResponse<R>

/* Utility Types */
type QueryResponse<T> = T extends [infer First, ...infer Rest]
  ? [RpcResponse<First>, ...QueryResponse<Rest>]
  : RpcResponse<any>[]

type PublicDatabases = PublicRuntimeConfig['surrealdb']['databases'];
interface ModuleOptions {
    auth: {
        adminMaxAge?: number;
        database?: keyof PublicDatabases | false;
        sessionName?: string;
        cookieName?: string;
        sameSite?: boolean | 'strict' | 'lax' | 'none';
    };
    databases: {
        default?: DatabasePreset;
        [key: string]: DatabasePreset | undefined;
    };
    server: {
        databases?: {
            [key: string]: DatabasePreset | undefined;
        };
    };
}
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;

interface SurrealServerOptions {
    surrealdb?: ModuleOptions['server'];
}
interface SurrealOptions {
    surrealdb?: Omit<ModuleOptions, 'server'>;
}
declare module '@nuxt/schema' {
    interface RuntimeConfig extends SurrealServerOptions {
    }
    interface PublicRuntimeConfig extends SurrealOptions {
    }
}

export { type AuthToken, type ComputedOptions, type DatabasePreset, type DatabasePresetKeys, type DatabasePresetServerKeys, type HttpRes, type HttpResponse, type HttpResponseError, type HttpResponseOk, type JSONPatch, type JSONPatchPath, type JSONPatchValue, type KeysOf$1 as KeysOf, type ModuleOptions, type Overrides, type PickFrom, type QueryResponse, type RpcMethods, type RpcMethodsWS, type RpcParams, type RpcParamsWS, type RpcRequest, type RpcRequestWS, type RpcResponse, type RpcResponseError, type RpcResponseErrorWS, type RpcResponseOk, type RpcResponseOkWS, type RpcResponseWS, type ServerOverrides, type SurrealAsyncDataOptions, type SurrealFetchOptions, type SurrealMethods, type UseSurrealFetchOptions, type UseSurrealRpcOptions, type User, type UserSession, type UserSessionComposable, type UserSessionRequired, _default as default, useSurrealFetch };
