import Config from "./config"

export type AutoReconnectOptions = boolean | {
    maxRetries?: number
    retryInterval?: number
    onMaxRetriesReached?: Function
  }
  
  export enum ConnectionStatus {
    Disconnected = 'DISCONNECTED',
    Connected = 'CONNECTED',
    Error = 'ERROR'
  }
  
  class SocketService {
    private static instance: SocketService | null = null
    private adminws: WebSocket | null = null
    private listeners: Record<string, Function[]> = {}
    private autoReconnect: AutoReconnectOptions = true
    private times: any = null
    private retries: number = 0
    private connectionStatus: ConnectionStatus = ConnectionStatus.Disconnected
  
    private constructor() {
      this.connect()
    }
  
    public static getInstance(): SocketService {
      if (!SocketService.instance) {
        SocketService.instance = new SocketService()
      }
      return SocketService.instance
    }
  
    public setAutoReconnectOptions(options: AutoReconnectOptions) {
      this.autoReconnect = options
    }
    
    public connect() {
      this.adminws = new WebSocket(Config.ws)
      this.adminws.onopen = () => {
        this.connectionStatus = ConnectionStatus.Connected
        this.emit('connected', null)
        this.hert()
      }
      this.adminws.onerror = () => {
        this.connectionStatus = ConnectionStatus.Error
        clearTimeout(this.times)
        this.emit('error', null)
      }
      this.adminws.onclose = () => {
        this.connectionStatus = ConnectionStatus.Disconnected
        clearTimeout(this.times)
        this.emit('disconnected', null)
        if (this.shouldReconnect()) {
          setTimeout(() => this.connect(), this.getRetryInterval())
        }
      }
      this.adminws.onmessage = (event) => {
        this.emit('message', event.data)
      }
    }
    public hert(){
        this.times = setInterval(() => this.send({"type":"heartbeat","data":{}}), 10000)
    }
    private shouldReconnect(): boolean {
      if (typeof this.autoReconnect === 'boolean') {
        return this.autoReconnect
      } else if (this.autoReconnect) {
        const { maxRetries } = this.autoReconnect
  
        if (maxRetries !== undefined) {
          if (this.retries < maxRetries) {
            this.retries++
            return true
          } else if (this.retries >= maxRetries) {
            this.autoReconnect.onMaxRetriesReached && this.autoReconnect.onMaxRetriesReached()
            return false
          }
        }
      }
      return false
    }
  
    private getRetryInterval(): number {
      if (typeof this.autoReconnect === 'boolean') {
        return 1000
      } else if (this.autoReconnect && this.autoReconnect.retryInterval) {
        return this.autoReconnect.retryInterval
      }
      return 1000
    }
  
    public send(data: any) {
      if (this.adminws && this.adminws.readyState === WebSocket.OPEN) {
        this.adminws.send(JSON.stringify(data))
      } else {
        console.error('WebSocket 连接未打开')
      }
    }
  
    public close() {
      if (this.adminws) {
        this.adminws.close()
        SocketService.instance = null
      }
    }
  
    private emit(event: string, data: any) {
      if (!this.listeners[event]) {
        return
      }
      this.listeners[event].forEach((listener) => listener(data))
    }
  
    public on(event: string, listener: Function) {
      if (!this.listeners[event]) {
        this.listeners[event] = []
      }
      this.listeners[event].push(listener)
      if (event === 'connected' && this.connectionStatus === ConnectionStatus.Connected) {
        listener()
      }
    }
  
    public off(event: string, listener: Function) {
      if (!this.listeners[event]) {
        return
      }
      this.listeners[event] = this.listeners[event].filter((l) => l !== listener)
    }
  
    public getConnectionStatus(): ConnectionStatus {
      return this.connectionStatus
    }
  
    public watchConnectionStatus(callback: (status: ConnectionStatus) => void) {
      this.on('connected', () => callback(ConnectionStatus.Connected))
      this.on('disconnected', () => callback(ConnectionStatus.Disconnected))
      this.on('error', () => callback(ConnectionStatus.Error))
    }
  }
  
  export default SocketService
  