import { loadNative } from '../../js';

const native = loadNative();

// Bridge test endpoints are only present when the native module was built with
// `--features bridge-test-harness` (e.g. `yarn native:build-debug-bridge-tests`).
// Test suites should gate themselves on this flag rather than calling the
// helpers blindly, so a regular debug build doesn't blow up the unit run.
export const bridgeHarnessAvailable: boolean =
  typeof native.__testBridgeParseArgsNames === 'function';

export type SqlTemplate = string | string[];

export function parseArgsNames(fn: Function): string[] {
  return native.__testBridgeParseArgsNames(fn);
}

export function invokeFilterParamsCallback(
  fn: Function,
  args: string[]
): string {
  return native.__testBridgeInvokeFilterParamsCallback(fn, args);
}

export type BridgeFieldKind = 'field' | 'call' | 'static';

export interface BridgeFieldMeta {
  name: string;
  jsName: string;
  kind: BridgeFieldKind;
  optional: boolean;
  vec: boolean;
}

export function listBridgeFields(name: string): BridgeFieldMeta[] {
  if (!bridgeHarnessAvailable) {
    throw new Error(
      'Bridge test harness is not built. Rebuild with `yarn native:build-debug-bridge-tests`.'
    );
  }
  return native.__testBridgeListFields(name);
}

export function listBridgeNames(): string[] {
  if (!bridgeHarnessAvailable) {
    throw new Error(
      'Bridge test harness is not built. Rebuild with `yarn native:build-debug-bridge-tests`.'
    );
  }
  return native.__testBridgeListBridgeNames();
}

export function parseBridge(name: string, obj: unknown): void {
  if (!bridgeHarnessAvailable) {
    throw new Error(
      'Bridge test harness is not built. Rebuild with `yarn native:build-debug-bridge-tests`.'
    );
  }
  native.__testBridgeParse(name, obj);
}

export function fieldNames(meta: BridgeFieldMeta[]): string[] {
  return meta.map((m) => m.name).sort();
}

export type InvokeStatus =
  | { status: 'ok' }
  | { status: 'error'; message: string }
  | { status: 'skipped'; reason: string };

export type InvokeResult = Record<string, InvokeStatus>;

export function invokeBridge(name: string, fixture: unknown): InvokeResult {
  if (!bridgeHarnessAvailable) {
    throw new Error(
      'Bridge test harness is not built. Rebuild with `yarn native:build-debug-bridge-tests`.'
    );
  }
  return native.__testBridgeInvoke(name, fixture);
}

/**
 * Asserts every recorded invocation is `ok` or `skipped`. Errors surface
 * the offending field, the Rust-side message, and the kind of failure so
 * they read naturally in CI logs. Skipped entries are allowed because some
 * call-methods take Rust-only argument types (e.g. `Rc<dyn MemberSql>`)
 * that have no auto-default.
 */
export function expectAllInvocationsOk(result: InvokeResult): void {
  const failures: string[] = [];
  for (const [field, entry] of Object.entries(result)) {
    if (entry.status === 'error') {
      failures.push(`${field}: error: ${entry.message}`);
    }
  }
  if (failures.length > 0) {
    throw new Error(
      `Bridge invocation failed for ${failures.length} field(s):\n  ${failures.join('\n  ')}`
    );
  }
}

export interface RustBoxProbeView {
  value: number;
  label: string;
  type_name: string;
}

export function createRustBoxProbe(value: number, label: string): unknown {
  return native.__testBridgeRustBoxCreate(value, label);
}

export function createRustBoxProbeAlt(note: string): unknown {
  return native.__testBridgeRustBoxCreateAlt(note);
}

export function unwrapRustBoxProbe(handle: unknown): RustBoxProbeView {
  return native.__testBridgeRustBoxUnwrap(handle);
}
