import './export-lupine';
import { describe, it, mock, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import {
  ComponentStateStore,
  evaluateComponentWithStore,
  getCurrentStore,
  useState
} from './use-state';
import { domUniqueId } from './render-attribute';

describe('use-state Core Engine', () => {
  beforeEach(() => {
    mock.method(Date, 'now', () => 1775190809080);
    domUniqueId(true); // wipe cleanly
  });

  afterEach(() => {
    mock.restoreAll();
  });
  describe('evaluateComponentWithStore', () => {
    it('should successfully evaluate a synchronous component and return VNode', async () => {
      const SyncComp = (props: { text: string }) => {
        return { type: 'div', props: { children: [props.text] } };
      };

      const store = new ComponentStateStore(SyncComp, { text: 'Sync World' });
      const result = await evaluateComponentWithStore(store);

      assert.equal(result.type, 'div');
      assert.deepEqual(result.props.children, ['Sync World']);
    });

    it('should successfully evaluate an asynchronous component and return VNode', async () => {
      const AsyncComp = async (props: { text: string }) => {
        await new Promise(r => setTimeout(r, 1)); // simulate async
        return { type: 'span', props: { children: [props.text] } };
      };

      const store = new ComponentStateStore(AsyncComp, { text: 'Async World' });
      const result = await evaluateComponentWithStore(store);

      assert.equal(result.type, 'span');
      assert.deepEqual(result.props.children, ['Async World']);
    });

    it('should set and clear the global currentStore during execution', async () => {
      let storeInsideComp: any = undefined;

      const TestComp = () => {
        storeInsideComp = getCurrentStore();
        return { type: 'div', props: {} };
      };

      assert.equal(getCurrentStore(), null); // Before

      const store = new ComponentStateStore(TestComp, {});
      await evaluateComponentWithStore(store);

      assert.equal(storeInsideComp, store); // Inside component: pointer should be active
      assert.equal(getCurrentStore(), null); // After evaluation: pointer must be cleared
    });

    it('should inject ref wrapper onto VNode if a hook is used', async () => {
      const HookComp = () => {
        useState(0); // This sets store.hookIndex > 0
        return { type: 'section', props: { 'data-id': 'hooked' } };
      };

      const store = new ComponentStateStore(HookComp, {});
      const result = await evaluateComponentWithStore(store);

      // The returned VNode should now have a ref object generated by buildStateRef
      assert.ok(result.props.ref, 'Ref should be injected because a hook was used');
      assert.equal(typeof result.props.ref.onLoad, 'function');
      assert.equal(typeof result.props.ref.refresh, 'function');
    });

    it('should inject ref wrapper onto VNode if user already passed a ref', async () => {
      const userRef = { current: null, referToCssId: 'test-global-css' };
      const RefComp = () => {
        // No hook used here, but user passes an existing ref
        return { type: 'div', props: { ref: userRef } };
      };

      const store = new ComponentStateStore(RefComp, {});
      const result = await evaluateComponentWithStore(store);

      assert.ok(result.props.ref);
      assert.equal(result.props.ref, userRef, 'It should merge into the original user ref object');
      assert.equal(result.props.ref.referToCssId, 'test-global-css');
      assert.equal(typeof result.props.ref.onLoad, 'function', 'Wrapper added successfully');
    });
  });
});
