import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
import delayedResolve from "../../src/utils/delayedResolve.ts";

describe('delayedResolve', () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.useRealTimers();
  });

  it('returns the resolved value of the promise', async () => {
    const resultPromise = delayedResolve(Promise.resolve('hello'), 100);
    await jest.advanceTimersByTimeAsync(100);
    expect(await resultPromise).toBe('hello');
  });

  it('waits at least minDelayMs before resolving', async () => {
    let resolved = false;
    const resultPromise = delayedResolve(Promise.resolve('fast'), 100);
    resultPromise.then(() => { resolved = true; });

    await jest.advanceTimersByTimeAsync(50);
    expect(resolved).toBe(false);

    await jest.advanceTimersByTimeAsync(50);
    expect(resolved).toBe(true);
  });

  it('resolves immediately after slow promise if it exceeds minDelayMs', async () => {
    let resolved = false;
    const slowPromise = new Promise<string>(resolve =>
      setTimeout(() => resolve('slow'), 150)
    );
    const resultPromise = delayedResolve(slowPromise, 50);
    resultPromise.then(() => { resolved = true; });

    await jest.advanceTimersByTimeAsync(50);
    expect(resolved).toBe(false);

    await jest.advanceTimersByTimeAsync(100);
    expect(resolved).toBe(true);
    expect(await resultPromise).toBe('slow');
  });

  it('propagates rejections from the promise', async () => {
    const error = new Error('test error');
    const rejectedPromise = Promise.reject(error);

    const resultPromise = delayedResolve(rejectedPromise, 100);
    const assertion = expect(resultPromise).rejects.toThrow('test error');

    await jest.advanceTimersByTimeAsync(100);

    await assertion;
  });

  it('works with different value types', async () => {
    const test = async <T>(value: T) => {
      const resultPromise = delayedResolve(Promise.resolve(value), 10);
      await jest.advanceTimersByTimeAsync(10);
      return resultPromise;
    };

    expect(await test(42)).toBe(42);
    expect(await test({ foo: 'bar' })).toEqual({ foo: 'bar' });
    expect(await test([1, 2, 3])).toEqual([1, 2, 3]);
    expect(await test(null)).toBeNull();
  });

  it('handles zero delay', async () => {
    const resultPromise = delayedResolve(Promise.resolve('instant'), 0);
    await jest.advanceTimersByTimeAsync(0);
    expect(await resultPromise).toBe('instant');
  });
});
