import { WxtLocationChangeEvent } from "./internal/custom-events.mjs";
import { ContentScriptDefinition } from "../types.mjs";

//#region src/utils/content-script-context.d.ts
/**
 * Implements
 * [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).
 * Used to detect and stop content script code when the script is invalidated.
 *
 * It also provides several utilities like `ctx.setTimeout` and
 * `ctx.setInterval` that should be used in content scripts instead of
 * `window.setTimeout` or `window.setInterval`.
 *
 * To create context for testing, you can use the class's constructor:
 *
 * ```ts
 * import { ContentScriptContext } from 'wxt/utils/content-scripts-context';
 *
 * test('storage listener should be removed when context is invalidated', () => {
 *   const ctx = new ContentScriptContext('test');
 *   const item = storage.defineItem('local:count', { defaultValue: 0 });
 *   const watcher = vi.fn();
 *
 *   const unwatch = item.watch(watcher);
 *   ctx.onInvalidated(unwatch); // Listen for invalidate here
 *
 *   await item.setValue(1);
 *   expect(watcher).toBeCalledTimes(1);
 *   expect(watcher).toBeCalledWith(1, 0);
 *
 *   ctx.notifyInvalidated(); // Use this function to invalidate the context
 *   await item.setValue(2);
 *   expect(watcher).toBeCalledTimes(1);
 * });
 * ```
 */
declare class ContentScriptContext implements AbortController {
  private readonly contentScriptName;
  readonly options?: Omit<ContentScriptDefinition, "main"> | undefined;
  private static SCRIPT_STARTED_MESSAGE_TYPE;
  private id;
  private abortController;
  private locationWatcher;
  constructor(contentScriptName: string, options?: Omit<ContentScriptDefinition, "main"> | undefined);
  get signal(): AbortSignal;
  abort(reason?: any): void;
  get isInvalid(): boolean;
  get isValid(): boolean;
  /**
   * Add a listener that is called when the content script's context is
   * invalidated.
   *
   * @example
   *   browser.runtime.onMessage.addListener(cb);
   *   const removeInvalidatedListener = ctx.onInvalidated(() => {
   *     browser.runtime.onMessage.removeListener(cb);
   *   });
   *   // ...
   *   removeInvalidatedListener();
   *
   * @returns A function to remove the listener.
   */
  onInvalidated(cb: () => void): () => void;
  /**
   * Return a promise that never resolves. Useful if you have an async function
   * that shouldn't run after the context is expired.
   *
   * @example
   *   const getValueFromStorage = async () => {
   *     if (ctx.isInvalid) return ctx.block();
   *
   *     // ...
   *   };
   */
  block<T>(): Promise<T>;
  /**
   * Wrapper around `window.setInterval` that automatically clears the interval
   * when invalidated.
   *
   * Intervals can be cleared by calling the normal `clearInterval` function.
   */
  setInterval(handler: () => void, timeout?: number): number;
  /**
   * Wrapper around `window.setTimeout` that automatically clears the interval
   * when invalidated.
   *
   * Timeouts can be cleared by calling the normal `setTimeout` function.
   */
  setTimeout(handler: () => void, timeout?: number): number;
  /**
   * Wrapper around `window.requestAnimationFrame` that automatically cancels
   * the request when invalidated.
   *
   * Callbacks can be canceled by calling the normal `cancelAnimationFrame`
   * function.
   */
  requestAnimationFrame(callback: FrameRequestCallback): number;
  /**
   * Wrapper around `window.requestIdleCallback` that automatically cancels the
   * request when invalidated.
   *
   * Callbacks can be canceled by calling the normal `cancelIdleCallback`
   * function.
   */
  requestIdleCallback(callback: IdleRequestCallback, options?: IdleRequestOptions): number;
  /**
   * Call `target.addEventListener` and remove the event listener when the
   * context is invalidated.
   *
   * Listeners can be canceled by calling the normal `removeEventListener`
   * function.
   *
   * Includes additional events useful for content scripts:
   *
   * - `"wxt:locationchange"` - Triggered when HTML5 history mode is used to
   *   change URL. Content scripts are not reloaded when navigating this way, so
   *   this can be used to reset the content script state on URL change, or run
   *   custom code.
   *
   * @example
   *   ctx.addEventListener(document, 'visibilitychange', () => {
   *     // ...
   *   });
   *   ctx.addEventListener(window, 'wxt:locationchange', () => {
   *     // ...
   *   });
   */
  addEventListener<TType extends keyof WxtWindowEventMap>(target: Window, type: TType, handler: (event: WxtWindowEventMap[TType]) => void, options?: AddEventListenerOptions): void;
  addEventListener<TType extends keyof DocumentEventMap>(target: Document, type: TType, handler: (event: DocumentEventMap[TType]) => void, options?: AddEventListenerOptions): void;
  addEventListener<TTarget extends EventTarget>(target: TTarget, ...params: Parameters<TTarget['addEventListener']>): void;
  /**
   * @internal
   * Abort the abort controller and execute all `onInvalidated` listeners.
   */
  notifyInvalidated(): void;
  stopOldScripts(): void;
  verifyScriptStartedEvent(event: CustomEvent): boolean;
  listenForNewerScripts(): void;
}
interface WxtWindowEventMap extends WindowEventMap {
  'wxt:locationchange': WxtLocationChangeEvent;
}
//#endregion
export { ContentScriptContext, WxtWindowEventMap };