import type { Transaction } from "@tailor-platform/sdk/kysely";
import type { Result } from "./result";
import type { CommandContext } from "./types";
import { requirePermission } from "./requirePermission";
import type { InsufficientPermissionError } from "./errors";

export type Command<TInput, TReturn extends Result<unknown, Error>> = (
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  db: Transaction<any>,
  input: TInput,
  ctx: CommandContext,
) => Promise<TReturn>;

type CommandResult<TReturn extends Result<unknown, Error>> =
  | TReturn
  | Result<never, InstanceType<typeof InsufficientPermissionError>>;

export function defineCommand<
  TDeps extends unknown[],
  TInput,
  TReturn extends Result<unknown, Error>,
>(
  permission: string,
  impl: (
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    db: Transaction<any>,
    input: TInput,
    ctx: CommandContext,
    ...deps: TDeps
  ) => Promise<TReturn>,
): (...deps: TDeps) => Command<TInput, CommandResult<TReturn>> {
  return (...deps) =>
    async (db, input, ctx) => {
      if (!db.isTransaction) {
        throw new Error("Commands require a transaction database");
      }
      const check = requirePermission(ctx, permission);
      if (!check.ok) return check;
      return impl(db, input, ctx, ...deps);
    };
}
