import { MessageFromServer, UserData } from './types.mjs';

/**
 * An events map is an interface that maps event names to their value, which
 * represents the type of the `on` listener.
 */
interface EventsMap {
  [event: string]: any;
}

/**
 * Returns a union type containing all the keys of an event map.
 */
type EventNames<Map extends EventsMap> = keyof Map & (string | symbol);

/**
 * Type of a listener of a user event or a reserved event. If `Ev` is in
 * `ReservedEvents`, the reserved event listener is returned.
 */
type ReservedListener<
  ReservedEvents extends EventsMap,
  Ev extends EventNames<ReservedEvents>
> = FallbackToUntypedListener<ReservedEvents[Ev]>;

/**
 * Returns an untyped listener type if `T` is `never`; otherwise, returns `T`.
 *
 * This is a hack to mitigate https://github.com/socketio/socket.io/issues/3833.
 * Needed because of https://github.com/microsoft/TypeScript/issues/41778
 */
type FallbackToUntypedListener<T> = [T] extends [never]
  ? () => void | Promise<void>
  : (data: T) => void | Promise<void>;

/**
 * Strictly typed version of an `EventEmitter`. A `TypedEventEmitter` takes type
 * parameters for mappings of event names to event data types, and strictly
 * types method calls to the `EventEmitter` according to these event maps.
 *
 * @typeParam ListenEvents - `EventsMap` of user-defined events that can be
 * listened to with `on` or `once`
 * @typeParam EmitEvents - `EventsMap` of user-defined events that can be
 * emitted with `emit`
 * @typeParam ReservedEvents - `EventsMap` of reserved events, that can be
 * emitted by socket.io with `emitReserved`, and can be listened to with
 * `listen`.
 */
declare class Emitter<ReservedEvents extends EventsMap = {}> {
  /**
   * Adds the `listener` function as an event listener for `event`.
   *
   * @param event Name of the event
   * @param listener Callback function
   */
  on<Ev extends EventNames<ReservedEvents>>(
    event: Ev,
    listener: ReservedListener<ReservedEvents, Ev>
  ): this;

  /**
   * Adds a one-time `listener` function as an event listener for `event`.
   *
   * @param event Name of the event
   * @param listener Callback function
   */
  once<Ev extends EventNames<ReservedEvents>>(
    event: Ev,
    listener: ReservedListener<ReservedEvents, Ev>
  ): this;

  /**
   * Removes the `listener` function as an event listener for `event`.
   *
   * @param event Name of the event
   * @param listener Callback function
   */
  off<Ev extends EventNames<ReservedEvents>>(
    event?: Ev,
    listener?: ReservedListener<ReservedEvents, Ev>
  ): this;

  /**
   * Emits an event.
   *
   * @param event Name of the event
   * @param args Values to send to listeners of this event
   */
  protected emit<Ev extends EventNames<ReservedEvents>>(
    event: Ev,
    ...data: ReservedEvents[Ev] extends never ? [undefined?] : [ReservedEvents[Ev]]
  ): this;

  /**
   * Returns the listeners listening to an event.
   *
   * @param event Event name
   * @returns Array of listeners subscribed to `event`
   */
  listeners<Ev extends EventNames<ReservedEvents>>(
    event: Ev
  ): ReservedListener<ReservedEvents, Ev>[];

  /**
   * Returns true if there is a listener for this event.
   *
   * @param event Event name
   * @returns boolean
   */
  hasListeners<Ev extends EventNames<ReservedEvents>>(event: Ev): boolean;

  /**
   * Removes the `listener` function as an event listener for `event`.
   *
   * @param event Name of the event
   * @param listener Callback function
   */
  removeListener<Ev extends EventNames<ReservedEvents>>(
    event?: Ev,
    listener?: ReservedListener<ReservedEvents, Ev>
  ): this;

  /**
   * Removes all `listener` function as an event listener for `event`.
   *
   * @param event Name of the event
   */
  removeAllListeners<Ev extends EventNames<ReservedEvents>>(event?: Ev): this;
}

type RequestUrl = Parameters<typeof fetch>[0];
type RequestOptions = Parameters<typeof fetch>[1];
interface AppOptions {
    customFetch?: typeof fetch;
    autoinit?: boolean;
}
declare class SPWMini extends Emitter<MessageFromServer> {
    #private;
    appId: string;
    isReady: boolean;
    user: UserData | null;
    customFetch: typeof fetch | null;
    constructor(appId: string, options?: AppOptions);
    initialize(): void;
    validateUser(url: RequestUrl, init?: RequestOptions): Promise<boolean>;
    dispose(): void;
    openURL(url: string): void;
    openPayment(code: string): void;
}

export { SPWMini as default };
