import { DiscordSDK } from "@discord/embedded-app-sdk";

export interface InitOptions {
  spectator?: boolean;
  streamMode?: boolean;
  liveMode?: LiveModeOptions;
  discord?: DiscordOptions | boolean;
  persistentMode?: boolean;
  allowGamepads?: boolean;
  baseUrl?: string;
  enableBots?: boolean;
  botOptions?: BotOptions;
  avatars?: string[];
  roomCode?: string;
  skipLobby?: boolean;
  reconnectGracePeriod?: number;
  maxPlayersPerRoom?: number;
  maxSpectatorsPerRoom?: number;
  defaultStates?: { [key: string]: any };
  defaultPlayerStates?: { [key: string]: any };
  websocketBaseUrl?: string;
  matchmaking?: MatchmakingOptions | boolean;
  turnBased?: TurnBasedOptions | boolean;
  gameId?: string;
  auth?: boolean;
}

export interface TurnBasedOptions {
  challengeId: string;
}

export type LiveModeOptions = "tiktok";

export interface DiscordOptions {
  prompt?: string;
  scope?: string[];
  state?: string;
}

export interface MatchmakingOptions {
  waitBeforeCreatingNewRoom?: number;
}
export interface Color {
  r: number;
  g: number;
  b: number;
  hexString: string;
  hex: number;
}

export interface PlayerProfile {
  name: string;
  color: Color;
  photo: string;
  avatarIndex: number;
}

export interface PlayerState {
  id: string;
  getProfile(): PlayerProfile;
  getState(key: string): any;
  setState(key: string, value: any, reliable?: boolean): void;
  onQuit: onQuit;
  kick(): void;
  leaveRoom(): void;
}

interface BotSpecs {
  gamma: number;
  epsilon: number;
  alpha: number;
  experience_add_every: number;
  experience_size: number;
  learning_steps_per_iteration: number;
  tderror_clamp: number;
  num_hidden_units: number;
}

interface BotOptions {
  botClass: new (...args: any[]) => Bot;
  botParams?: Object;
  trainingMode?: boolean;
}

export interface Bot {
  id: string;
  getProfile(): PlayerProfile;
  getState(key: string): any;
  setState(key: string, value: any, reliable?: boolean): void;
  onQuit: onQuit;
  kick(): void;
  leaveRoom(): void;
}

export interface JoystickOptions {
  type: "dpad" | "angular";
  buttons?: ButtonOptions[];
  zones?: ZoneOptions;
  keyboard?: boolean;
}

export interface ButtonOptions {
  id: string;
  label?: string;
  icon?: string;
}

export interface ZoneOptions {
  up?: ButtonOptions;
  down?: ButtonOptions;
  left?: ButtonOptions;
  right?: ButtonOptions;
}

export interface Joystick {
  isPressed(id: string): boolean;
  isJoystickPressed(): boolean;
  angle(): number;
  dpad(): { x: "left" | "right"; y: "up" | "down" };
  destroy(): void;
}

export declare function getRoomCode(): string | undefined;
export declare function insertCoin(
  options?: InitOptions,
  callbackOnLaunch?: () => void,
  callbackOnError?: (error: Error) => void
): Promise<void>;

export declare function startAd(
  actionButtonClick: () => void,
  completeCallback: () => void,
  skipCallback: () => void,
  errorCallback: () => void
): void;

export declare function getState(key: string): any;
export declare function setState(
  key: string,
  value: any,
  reliable?: boolean
): void;

/**
 * Register a callback that will be called when a new player joins the room.
 * @param {function} callback This function runs when a player joins the game.
 * @returns {function} Cleanup function for the "onPlayerJoin" event listener.
 */
export declare function onPlayerJoin(
  callback: (player: PlayerState) => void
): () => void;

export declare function isHost(): boolean;
export declare function isStreamScreen(): boolean;
export declare function isSpectator(): boolean;
export declare function myPlayer(): PlayerState;
export declare function getSpectators(): Record<string, PlayerState>;
export declare function getParticipants(): Record<string, PlayerState>;
export declare function me(): PlayerState;
export declare function waitForState<T>(
  stateKey: string,
  callbackOnState?: (stateValue: T) => void
): Promise<T>;
export declare function waitForPlayerState<T>(
  player: PlayerState,
  stateKey: string,
  callbackOnState?: (stateValue: T) => void
): Promise<T>;
export declare class Joystick {
  constructor(state: PlayerState, options: JoystickOptions);
}
export declare class Bot {
  constructor(botParams: Object);
}

// REACT HOOKS
type SetStateFunction<T> = (value: T, reliable?: boolean) => void;
type MultiplayerStateHookResult<T> = [T, SetStateFunction<T>];

export declare function useMultiplayerState<T = any>(
  key: string,
  defaultValue: T
): MultiplayerStateHookResult<T>;

export declare function usePlayerState<T = any>(
  player: PlayerState,
  key: string,
  defaultValue: T
): MultiplayerStateHookResult<T>;

export declare function usePlayersList(
  triggerOnPlayerStateChange?: boolean
): PlayerState[];

export declare function useSpectatorsList(
  triggerOnPlayerStateChange?: boolean
): PlayerState[];

export declare function useParticipantsList(
  triggerOnPlayerStateChange?: boolean
): PlayerState[];

interface PlayerStateWithValue {
  player: PlayerState;
  state: any;
}

export declare function usePlayersState(key: string): PlayerStateWithValue[];

export declare function useSpectatorsState(key: string): PlayerStateWithValue[];

export declare function useParticipantsState(key: string): PlayerStateWithValue[];


export declare function useIsHost(): boolean;

/**
 * Listen for the quit event for the player in the room.
 * @param {function} callback This function runs when a player quits the game and leaves the room.
 * @returns {function} Cleanup function for the "onQuit" event listener.
 */
type onQuit = (callback: (player: PlayerState) => void) => () => void;

export declare function addBot(): Promise<Bot>;

export type TikTokLiveEvent = {
  type: "chat" | "gift" | "like" | "subscribe" | "streamEnd";
  data: {
    t: number;
    msgId: string;
    userId: string;
    name: string;
    userPhotoUrl: string;

    comment?: string;
    giftName?: string;
    giftPhotoUrl?: string;
    giftId?: string;
    giftDiamondCount?: number;
  };
};

export type TikTokLiveEventCallback = (param: TikTokLiveEvent) => void;

export declare function onTikTokLiveEvent(
  callback: TikTokLiveEventCallback
): () => void;

export type DisconnectEvent = {
  code: number;
  reason: string;
};

/**
 * Listen for the disconnect event for current player.
 * @param {function} callback This function runs when the player disconnects from the room.
 * @returns {function} Cleanup function for the "onDisconnect" event listener.
 */

export declare function onDisconnect(
  callback: (event: DisconnectEvent) => void
): () => void;

export declare function resetStates(keysToExclude?: string[]): Promise<void>;
export declare function resetPlayersStates(
  keysToExclude?: string[]
): Promise<void>;

declare enum RPCMode {
  ALL = 0,
  OTHERS = 1,
  HOST = 2,
}

declare type RPCCallback = (
  payload: any,
  senderPlayer: PlayerState,
  mode: RPCMode
) => Promise<any>;

/**
 * Register a callback that will be called when this RPC is called.
 * @param {string} name The name of the RPC.
 * @param {function} callback This function runs when the RPC is called.
 * @returns {function} Cleanup function for the "register" event listener.
 */
declare type RPCRegister = (name: string, callback: RPCCallback) => () => void;

declare type RPCCall = (
  name: string,
  payload: any,
  mode?: RPCMode,
  callbackOnResponse?: (response: any) => void
) => Promise<any>;

export const RPC: {
  Mode: typeof RPCMode;
  register: RPCRegister;
  call: RPCCall;
};

export type { RPCCallback, RPCRegister, RPCCall, RPCMode };

export declare function setPersistentData(
  key: string,
  value: any
): Promise<any>;

export declare function getPersistentData(key: string): Promise<any>;

export declare function insertPersistentData(
  key: string,
  value: any
): Promise<number>;

export declare function startMatchmaking(): Promise<void>;
export declare function transferHost(playerId:string): Promise<void>;
export declare function switchRole(): Promise<void>;

export declare function getChallengeId(): string;
export declare function saveMyTurnData(data: any): Promise<void>;
export declare function getAllTurns(): Promise<Array<any>>;
export declare function getMyTurnData(): Promise<any>;
export declare function clearTurns(): Promise<void>;

export declare function openDiscordInviteDialog(): Promise<void>;
export declare function getDiscordClient(): DiscordSDK | undefined;
export declare function setDiscordServerData(
  key: string,
  value: any
): Promise<any>;

export declare function getDiscordServerData(key: string): Promise<any>;

export declare function insertDiscordServerData(
  key: string,
  value: any
): Promise<number>;

export declare function getDiscordAccessToken(): string | undefined;

export declare function getDiscordAuth():
  | {
      access_token: string;
      user: {
        username: string;
        discriminator: string;
        id: string;
        public_flags: number;
        avatar?: string | null | undefined;
        global_name?: string | null | undefined;
      };
      scopes: string[];
      expires: string;
      application: {
        id: string;
        description: string;
        name: string;
        icon?: string | null | undefined;
        rpc_origins?: string[] | undefined;
      };
    }
  | undefined;

export declare function getDiscordSDK(): Promise<any>;
