import type { QueryContext } from "./types";
import type { Result } from "./result";
import { requirePermission } from "./requirePermission";
import type { InsufficientPermissionError } from "./errors";

export type Query<TInput, TResult> = (
  db: unknown,
  input: TInput,
  ctx: QueryContext,
) => Promise<TResult>;

type QueryResult<TReturn> =
  TReturn extends Result<infer _V, infer _E>
    ? TReturn | Result<never, InstanceType<typeof InsufficientPermissionError>>
    : TReturn;

// Overload: no permission
export function defineQuery<TInput, TResult>(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  impl: (db: any, input: TInput, ctx: QueryContext) => Promise<TResult>,
): Query<TInput, TResult>;

// Overload: with permission
export function defineQuery<TInput, TResult>(
  permission: string,
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  impl: (db: any, input: TInput, ctx: QueryContext) => Promise<TResult>,
): Query<TInput, QueryResult<TResult>>;

// Implementation
export function defineQuery<TInput, TResult>(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  permissionOrImpl: string | ((db: any, input: TInput, ctx: QueryContext) => Promise<TResult>),
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  maybeImpl?: (db: any, input: TInput, ctx: QueryContext) => Promise<TResult>,
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
): Query<TInput, any> {
  if (typeof permissionOrImpl === "string") {
    const permission = permissionOrImpl;
    const impl = maybeImpl!;
    return async (db, input, ctx) => {
      const check = requirePermission(ctx, permission);
      if (!check.ok) return check;
      return impl(db, input, ctx);
    };
  }
  const impl = permissionOrImpl;
  return async (db, input, ctx) => {
    return impl(db, input, ctx);
  };
}
