/* eslint-disable no-extend-native -- 该文件用于集中安装旧浏览器缺失的原生 API。 */

import { deserialize, serialize } from '@ungap/structured-clone';

type AtFunction = (index: number) => unknown;
type StructuredCloneFunction = <T>(
  value: T,
  options?: StructuredSerializeOptions,
) => T;
type WithResolversResult<T> = {
  promise: Promise<T>;
  reject: (reason?: unknown) => void;
  resolve: (value: PromiseLike<T> | T) => void;
};

const objectConstructor = Object as ObjectConstructor & {
  hasOwn?: (object: unknown, property: PropertyKey) => boolean;
};
const arrayPrototype = Array.prototype as unknown[] & {
  at?: AtFunction;
  toReversed?: () => unknown[];
  toSorted?: (
    compareFn?: (left: unknown, right: unknown) => number,
  ) => unknown[];
  toSpliced?: (
    start: number,
    deleteCount?: number,
    ...items: unknown[]
  ) => unknown[];
};
const stringPrototype = String.prototype as string & {
  at?: AtFunction;
};
const promiseConstructor = Promise as PromiseConstructor & {
  withResolvers?: <T>() => WithResolversResult<T>;
};
const urlConstructor = URL as typeof URL & {
  canParse?: (url: string | URL, base?: string | URL) => boolean;
};
const browserGlobal = globalThis as typeof globalThis & {
  structuredClone?: StructuredCloneFunction;
};

/** 将索引转换为 at 方法使用的整数。 */
function toIntegerOrInfinity(index: number): number {
  const numericIndex = Number(index);
  if (Number.isNaN(numericIndex) || numericIndex === 0) return 0;
  return numericIndex > 0 ? Math.floor(numericIndex) : Math.ceil(numericIndex);
}

/** 安装 Object.hasOwn 兼容实现。 */
export function installObjectHasOwnPolyfill() {
  if (typeof objectConstructor.hasOwn === 'function') return;
  Object.defineProperty(Object, 'hasOwn', {
    configurable: true,
    value(object: unknown, property: PropertyKey) {
      if (object === null || object === undefined) {
        throw new TypeError('Cannot convert undefined or null to object');
      }
      // eslint-disable-next-line unicorn/new-for-builtins -- Object() 保留 ToObject 语义。
      return Object.prototype.hasOwnProperty.call(Object(object), property);
    },
    writable: true,
  });
}

/** 安装 Array.prototype.at 兼容实现。 */
export function installArrayAtPolyfill() {
  if (typeof arrayPrototype.at === 'function') return;
  Object.defineProperty(Array.prototype, 'at', {
    configurable: true,
    value(this: ArrayLike<unknown> | null | undefined, index: number): unknown {
      if (this === null || this === undefined) {
        throw new TypeError('Array.prototype.at called on null or undefined');
      }
      const target = this as ArrayLike<unknown>;
      const numericLength = Number(target.length);
      const length =
        Number.isNaN(numericLength) || numericLength <= 0
          ? 0
          : Math.min(Math.floor(numericLength), Number.MAX_SAFE_INTEGER);
      const relativeIndex = toIntegerOrInfinity(index);
      const actualIndex =
        relativeIndex >= 0 ? relativeIndex : length + relativeIndex;
      return actualIndex < 0 || actualIndex >= length
        ? undefined
        : target[actualIndex];
    },
    writable: true,
  });
}

/** 安装 String.prototype.at 兼容实现。 */
export function installStringAtPolyfill() {
  if (typeof stringPrototype.at === 'function') return;
  Object.defineProperty(String.prototype, 'at', {
    configurable: true,
    value(this: unknown, index: number): string | undefined {
      if (this === null || this === undefined) {
        throw new TypeError('String.prototype.at called on null or undefined');
      }
      const target = String(this);
      const relativeIndex = toIntegerOrInfinity(index);
      const actualIndex =
        relativeIndex >= 0 ? relativeIndex : target.length + relativeIndex;
      return actualIndex < 0 || actualIndex >= target.length
        ? undefined
        : target[actualIndex];
    },
    writable: true,
  });
}

/** 安装不会修改原数组的数组复制方法。 */
export function installArrayCopyingPolyfills() {
  if (typeof arrayPrototype.toReversed !== 'function') {
    Object.defineProperty(Array.prototype, 'toReversed', {
      configurable: true,
      value() {
        return Array.prototype.slice.call(this).reverse();
      },
      writable: true,
    });
  }
  if (typeof arrayPrototype.toSorted !== 'function') {
    Object.defineProperty(Array.prototype, 'toSorted', {
      configurable: true,
      value(compareFn?: (left: unknown, right: unknown) => number) {
        return Array.prototype.slice.call(this).sort(compareFn);
      },
      writable: true,
    });
  }
  if (typeof arrayPrototype.toSpliced !== 'function') {
    Object.defineProperty(Array.prototype, 'toSpliced', {
      configurable: true,
      value(start: number, deleteCount?: number, ...items: unknown[]) {
        const copy = Array.prototype.slice.call(this);
        if (arguments.length === 1) copy.splice(start);
        else copy.splice(start, deleteCount as number, ...items);
        return copy;
      },
      writable: true,
    });
  }
}

/** 安装 Promise.withResolvers 兼容实现。 */
export function installPromiseWithResolversPolyfill() {
  // eslint-disable-next-line n/no-unsupported-features/es-syntax -- 此处专门检测并补齐该新 API。
  if (typeof promiseConstructor.withResolvers === 'function') return;
  Object.defineProperty(Promise, 'withResolvers', {
    configurable: true,
    value<T>(this: PromiseConstructor): WithResolversResult<T> {
      let reject!: (reason?: unknown) => void;
      let resolve!: (value: PromiseLike<T> | T) => void;
      const promise = new this<T>((resolvePromise, rejectPromise) => {
        resolve = resolvePromise;
        reject = rejectPromise;
      });
      return { promise, reject, resolve };
    },
    writable: true,
  });
}

/** 安装可处理复杂值与循环引用的 structuredClone 兼容实现。 */
export function installStructuredClonePolyfill() {
  if (typeof browserGlobal.structuredClone === 'function') return;
  Object.defineProperty(globalThis, 'structuredClone', {
    configurable: true,
    value: <T>(value: T) => deserialize(serialize(value)) as T,
    writable: true,
  });
}

/** 安装 URL.canParse 兼容实现。 */
export function installUrlCanParsePolyfill() {
  if (typeof urlConstructor.canParse === 'function') return;
  Object.defineProperty(URL, 'canParse', {
    configurable: true,
    value(url: string | URL, base?: string | URL) {
      try {
        const parsedUrl =
          arguments.length > 1
            ? new URL(String(url), String(base))
            : new URL(String(url));
        return parsedUrl instanceof URL;
      } catch {
        return false;
      }
    },
    writable: true,
  });
}

/** 在业务模块加载前安装 Chrome 90 缺失的运行时能力。 */
export function installLegacyBrowserPolyfills() {
  installObjectHasOwnPolyfill();
  installArrayAtPolyfill();
  installStringAtPolyfill();
  installArrayCopyingPolyfills();
  installPromiseWithResolversPolyfill();
  installStructuredClonePolyfill();
  installUrlCanParsePolyfill();
}

installLegacyBrowserPolyfills();
