import { Effect } from "effect";
import type * as EffectModule from "effect/Effect";

import { type SupabaseApiClientShape, type SupabaseApiError, SupabaseApiClient } from "./client.ts";

export type EffectClient<Operations extends object> = {
  readonly [Key in keyof Operations]: Operations[Key] extends (
    ...args: infer Args
  ) => EffectModule.Effect<infer Output, infer Error, SupabaseApiClient>
    ? (...args: Args) => EffectModule.Effect<Output, Error>
    : Operations[Key] extends object
      ? EffectClient<Operations[Key]>
      : never;
};

function isRecord(value: unknown): value is Readonly<Record<PropertyKey, unknown>> {
  return typeof value === "object" && value !== null;
}

export function makeEffectApiClient<Operations extends object>(
  client: SupabaseApiClientShape,
  operations: Operations,
): EffectClient<Operations> {
  const bindOperation = (value: unknown): unknown => {
    if (typeof value === "function") {
      return (...args: ReadonlyArray<unknown>) =>
        (
          value as (
            ...args: ReadonlyArray<unknown>
          ) => Effect.Effect<unknown, SupabaseApiError, SupabaseApiClient>
        )(...args).pipe(Effect.provideService(SupabaseApiClient, client));
    }
    if (isRecord(value)) {
      return Object.fromEntries(
        Object.entries(value).map(([key, entry]) => [key, bindOperation(entry)]),
      );
    }
    return value;
  };

  return bindOperation(operations) as EffectClient<Operations>;
}