import { afterEach, beforeEach, describe, expect, it } from 'vitest';

import {
  installArrayAtPolyfill,
  installObjectHasOwnPolyfill,
  installPromiseWithResolversPolyfill,
  installStringAtPolyfill,
  installStructuredClonePolyfill,
  installUrlCanParsePolyfill,
} from './polyfills';

const descriptors = new Map<
  string,
  [object, PropertyKey, PropertyDescriptor | undefined]
>();

function remember(key: string, target: object, property: PropertyKey) {
  descriptors.set(key, [
    target,
    property,
    Object.getOwnPropertyDescriptor(target, property),
  ]);
}

function remove(target: object, property: PropertyKey) {
  Reflect.deleteProperty(target, property);
}

beforeEach(() => {
  remember('arrayAt', Array.prototype, 'at');
  remember('stringAt', String.prototype, 'at');
  remember('hasOwn', Object, 'hasOwn');
  remember('withResolvers', Promise, 'withResolvers');
  remember('structuredClone', globalThis, 'structuredClone');
  remember('canParse', URL, 'canParse');
});

afterEach(() => {
  for (const [target, property, descriptor] of descriptors.values()) {
    if (descriptor) Object.defineProperty(target, property, descriptor);
    else remove(target, property);
  }
  descriptors.clear();
});

describe('chrome 90 browser polyfills', () => {
  it('installs Array.prototype.at and String.prototype.at', () => {
    remove(Array.prototype, 'at');
    remove(String.prototype, 'at');

    installArrayAtPolyfill();
    installStringAtPolyfill();

    expect(['first', 'last'].at(-1)).toBe('last');
    expect('EasyFlow'.at(-1)).toBe('w');
    expect('EasyFlow'.at(99)).toBeUndefined();
  });

  it('installs Object.hasOwn without accepting inherited properties', () => {
    remove(Object, 'hasOwn');
    installObjectHasOwnPolyfill();

    const value = Object.create({ inherited: true }) as { own?: boolean };
    value.own = true;
    expect(Object.hasOwn(value, 'own')).toBe(true);
    expect(Object.hasOwn(value, 'inherited')).toBe(false);
  });

  it('installs Promise.withResolvers and exposes working callbacks', async () => {
    remove(Promise, 'withResolvers');
    installPromiseWithResolversPolyfill();

    // eslint-disable-next-line n/no-unsupported-features/es-syntax -- 测试目标就是验证该 API 的兼容实现。
    const { promise, resolve } = Promise.withResolvers<string>();
    resolve('ready');
    await expect(promise).resolves.toBe('ready');
  });

  it('installs structuredClone with complex value and cycle support', () => {
    remove(globalThis, 'structuredClone');
    installStructuredClonePolyfill();

    const source: {
      createdAt: Date;
      lookup: Map<string, number>;
      self?: unknown;
    } = {
      createdAt: new Date('2026-07-15T00:00:00.000Z'),
      lookup: new Map([['answer', 2026]]),
    };
    source.self = source;
    const clone = structuredClone(source);

    expect(clone).not.toBe(source);
    expect(clone.self).toBe(clone);
    expect(clone.createdAt).toEqual(source.createdAt);
    expect(clone.lookup.get('answer')).toBe(2026);
  });

  it('installs URL.canParse for absolute and base-relative URLs', () => {
    remove(URL, 'canParse');
    installUrlCanParsePolyfill();

    expect(URL.canParse('https://easyflow.example/chat')).toBe(true);
    expect(URL.canParse('/chat', 'https://easyflow.example')).toBe(true);
    expect(URL.canParse('/chat')).toBe(false);
  });
});
