import { VaultfireSDKv3 } from "./v3/client";
import type { VaultfireV3Config } from "./v3/types";

export type ExplicitWriteVersion = "v2" | "v3";

export interface V2CompatibilityClient {
  read<T>(operation: string, args?: unknown): Promise<T>;
  write<T>(operation: string, args: unknown): Promise<T>;
}

export interface V3VersionClient {
  read<T>(operation: string, args?: unknown): Promise<T>;
  write<T>(operation: string, args: unknown): Promise<T>;
}

export type VersionedReadResult<TV2, TV3> =
  | { version: "v2"; data: TV2 }
  | { version: "v3"; data: TV3 };

/**
 * Explicit version router. It never silently redirects writes.
 *
 * The V2 adapter wraps the existing, unchanged V2 SDK. The V3 adapter wraps
 * the V3 modules or a transport implementation. Callers must select a version
 * for every mutation.
 */
export class VaultfireSDK {
  constructor(
    readonly v2: V2CompatibilityClient,
    readonly v3: V3VersionClient,
  ) {}

  async read<TV2, TV3>(
    version: "v2",
    operation: string,
    args?: unknown,
  ): Promise<VersionedReadResult<TV2, TV3>>;
  async read<TV2, TV3>(
    version: "v3",
    operation: string,
    args?: unknown,
  ): Promise<VersionedReadResult<TV2, TV3>>;
  async read<TV2, TV3>(
    version: ExplicitWriteVersion,
    operation: string,
    args?: unknown,
  ): Promise<VersionedReadResult<TV2, TV3>> {
    if (version === "v2") {
      return { version, data: await this.v2.read<TV2>(operation, args) };
    }
    return { version, data: await this.v3.read<TV3>(operation, args) };
  }

  async write<T>(
    version: ExplicitWriteVersion,
    operation: string,
    args: unknown,
  ): Promise<{ version: ExplicitWriteVersion; data: T }> {
    if (version === "v2") {
      return { version, data: await this.v2.write<T>(operation, args) };
    }
    return { version, data: await this.v3.write<T>(operation, args) };
  }
}

export function createVaultfireSDK(
  v2: V2CompatibilityClient,
  v3: V3VersionClient,
): VaultfireSDK {
  return new VaultfireSDK(v2, v3);
}

export function createV3Client(config: VaultfireV3Config): VaultfireSDKv3 {
  return new VaultfireSDKv3(config);
}
