// Mock react-native BEFORE importing the SDK — `../Nami` resolves
// the native module via TurboModuleRegistry at import time.
const reset = jest.fn().mockResolvedValue(undefined);
// The wrapper reads `result?.success` (see Nami.ts), so the mock must
// return that shape — not a plain boolean.
const configure = jest.fn().mockResolvedValue({ success: true });
const sdkConfigured = jest.fn().mockResolvedValue(false);
const sdkVersion = jest.fn().mockResolvedValue('test');

jest.mock('react-native', () => ({
  TurboModuleRegistry: {
    getEnforcing: jest.fn(() => ({
      reset,
      configure,
      sdkConfigured,
      sdkVersion,
    })),
  },
  NativeModules: {
    RNNami: { reset, configure, sdkConfigured, sdkVersion },
  },
}));

import { Nami } from '../Nami';

describe('Nami.reset (React Native bridge)', () => {
  beforeEach(() => {
    reset.mockClear();
    configure.mockClear();
  });

  it('exposes reset() as an async function on the JS surface', () => {
    expect(typeof Nami.reset).toBe('function');
  });

  it('delegates to the native RNNami.reset() turbomodule method', async () => {
    await Nami.reset();
    expect(reset).toHaveBeenCalledTimes(1);
  });

  it('returns a promise that resolves once the native call settles', async () => {
    const result = Nami.reset();
    expect(result).toBeInstanceOf(Promise);
    await expect(result).resolves.toBeUndefined();
  });

  it('propagates native bridge rejections to the JS caller', async () => {
    const err = new Error('native reset failed');
    reset.mockRejectedValueOnce(err);
    await expect(Nami.reset()).rejects.toThrow('native reset failed');
  });

  // NAM-1086: pre-fix, calling Nami.reset() before Nami.configure() on
  // Android crashed inside `NamiEntitlementManager`'s class-init block,
  // which read `Nami.refs.context` (a `lateinit`). The native bridge's
  // `try { Nami.reset(...) } catch (e: Throwable) { promise.reject(...) }`
  // turned that into a JS-observable `ExceptionInInitializerError`
  // rejection of the `Nami.reset()` promise. These tests pin the JS-side
  // contract: regardless of order or repetition, the bridge invocation
  // must resolve cleanly and the host app must not see a rejection.
  describe('order-of-calls (NAM-1086)', () => {
    it('does not reject when called before configure', async () => {
      // No prior `Nami.configure()` — simulates the cold-start path the
      // RN host app exercises when wiping persisted state on launch.
      await expect(Nami.reset()).resolves.toBeUndefined();
      expect(reset).toHaveBeenCalledTimes(1);
      expect(configure).not.toHaveBeenCalled();
    });

    it('does not reject when called after configure', async () => {
      await Nami.configure({} as any);
      await expect(Nami.reset()).resolves.toBeUndefined();
      expect(configure).toHaveBeenCalledTimes(1);
      expect(reset).toHaveBeenCalledTimes(1);
    });

    it('is idempotent across repeated cold calls', async () => {
      // The host app may bind `Nami.reset()` to a "Clear Data" button
      // that the user can tap multiple times in a row — each tap must
      // resolve cleanly.
      await expect(Nami.reset()).resolves.toBeUndefined();
      await expect(Nami.reset()).resolves.toBeUndefined();
      await expect(Nami.reset()).resolves.toBeUndefined();
      expect(reset).toHaveBeenCalledTimes(3);
    });

    it('does not reject when interleaved with configure', async () => {
      // Realistic flow: reset cold → configure → reset again to wipe
      // post-configure state. Each step must resolve.
      await expect(Nami.reset()).resolves.toBeUndefined();
      await expect(Nami.configure({} as any)).resolves.toBe(true);
      await expect(Nami.reset()).resolves.toBeUndefined();
      expect(reset).toHaveBeenCalledTimes(2);
      expect(configure).toHaveBeenCalledTimes(1);
    });
  });
});
