import { EventEmitter } from "events";
import ReconnectingWebSocket, {
  CloseEvent,
  ErrorEvent,
  Event,
  Message,
  Options,
} from "reconnecting-websocket";

export interface ConnectionEvents {
  open: [Event];
  close: [CloseEvent];
  error: [ErrorEvent];
  message: [MessageEvent];
}
export enum ConnectionState {
  Connected,
  NotConnected,
}
export type ConnectionOptions = Omit<
  Options,
  "WebSocket" | "maxEnqueuedMessages" | "startClosed" | "debug"
>;

const DEFAULT_OPTIONS: ConnectionOptions = {
  // 重连时间间隔（从1s随着重连次数增加逐渐到3s）
  maxReconnectionDelay: 3000,
  minReconnectionDelay: 1000,
  // 重连时间间隔增长速度
  reconnectionDelayGrowFactor: 1.3,
  // 多久时间认为链接稳定
  minUptime: 5000,
  // 建连超时时间
  connectionTimeout: 5000,
  // 重连次数
  maxRetries: 0,
};

export class Connection extends EventEmitter {
  connectCount = 0;
  state: ConnectionState = ConnectionState.NotConnected;
  private rws: ReconnectingWebSocket | null = null;

  connect = (url: string, options?: ConnectionOptions) => {
    try {
      this.rws = new ReconnectingWebSocket(url, [], { ...DEFAULT_OPTIONS, ...options });
      this.rws.binaryType = "arraybuffer";
      this.connectCount++;
      // 事件注册
      this.rws.addEventListener("open", this.onOpen);
      this.rws.addEventListener("close", this.onClose);
      this.rws.addEventListener("error", this.onError);
      this.rws.addEventListener("message", this.onMessage);
    } catch (error: any) {
      const errorEvent: ErrorEvent = {
        type: "error",
        target: this.rws,
        message: this.connectCount < 3 ? "unConnect" : error,
        error,
      };
      this.emit("error", errorEvent);
    }
  };

  reconnect = (url: string) => {
    if (!this.rws) return;
    this.rws.reconnect();
  };

  close = () => {
    if (!this.rws) return;
    this.rws.close();
  };

  send = (data: Message) => {
    if (!this.rws) return;
    this.rws.send(data);
  };

  isConnected() {
    return this.state === ConnectionState.Connected;
  }

  private onOpen = (event: Event) => {
    this.state = ConnectionState.Connected;
    this.emit("open", event);
  };

  private onClose = (event: CloseEvent) => {
    this.state = ConnectionState.NotConnected;
    this.emit("close", event);
  };

  private onError = (event: ErrorEvent) => {
    console.log("error", this.connectCount);
    this.emit("error", event);
  };

  private onMessage = (event: MessageEvent) => {
    this.emit("message", event);
  };
}
