import { fetchEventSource } from '@microsoft/fetch-event-source'
import { useAuthStore } from '@/store'

/**
 * @description: 创建sse连接
 */
class ServerSentEvents {
  static defaultConfig = {
    base: import.meta.env.VITE_BASE_API, // 基础地址
    url: '/sse', // 地址
    data: undefined, // 请求正文
    params: undefined, // 请求参数
    method: 'get', // 提交方式
    auth: false, // 是否携带token
    json: true, // 是否返回json
    returnData: true, // json数据是否返回data属性
    reconnect: true, //是否重连
    headers: {
      'Content-Type': 'application/json'
    },
    onopen: () => { },
    onmessage: () => { },
    onerror: () => { },
    onclose: () => { }
  }

  constructor(config) {
    if (config) {
      this.setConfig(config)
      this.init()
    }
  }

  static get(url, onmessage = () => { }, config = {}) {
    config.onmessage = onmessage
    config.url = url
    return new ServerSentEvents(config)
  }

  static post(url, data, onmessage = () => { }, config = {}) {
    config.onmessage = onmessage
    config.url = url
    config.method = 'post'
    config.data = data
    return new ServerSentEvents(config)
  }

  setConfig(config) {
    this.config = {
      ctrl: new AbortController(),
      ...ServerSentEvents.defaultConfig,
      ...config
    }
  }
  init() {
    if (this.config.auth) {
      this.headers.Authorization = 'Bearer ' + new useAuthStore().token
    }
    let url = this.config.url
    // 如果url不含协议
    if (url.indexOf("//") == -1) {
      url = this.config.base + url
    }

    if (this.config.params) {
      if (url.indexOf("?") > -1) {
        url += '&' + this.params(this.config.params)
      } else {
        url += '?' + this.params(this.config.params)
      }
    }

    let body = undefined

    if (this.config.data && (this.config.method === 'post' || this.config.method === 'put')) {
      if (this.config.data.constructor == URLSearchParams) {
        this.config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
        body = this.params(this.config.data).toString()
      } else if (this.config.data.constructor == FormData) {
        this.config.headers['Content-Type'] = 'multipart/form-data'
        body = this.config.data
      } else {
        body = JSON.stringify(body)
      }
    }
    this.config._url = url
    this.config._body = body
    console.debug(this.config)

    this.send()

  }

  send() {
    fetchEventSource(this.config._url, {
      method: this.config.method,
      headers: this.config.headers,
      body: this.config._body,
      signal: this.config.ctrl.signal,
      onopen: this.config.onopen,
      onmessage: (msg) => {
        if (this.config.json) {
          let data = JSON.parse(msg.data)
          if (this.config.returnData) {
            data = data.data
          }
          this.config.onmessage(data)
        } else {
          this.config.onmessage(msg)
        }
      },
      onclose: () => {
        console.info('onclose')
        this.abort()
        this.config.onclose()
        if (this.config.reconnect) {
          this.send()
        }
      },
      onerror: (err) => {
        console.error(err)
        this.abort()
        this.config.onerror(err)
      }
    })
  }

  abort() {
    if (this.config.ctrl && !this.config.reconnect) {
      try {
        this.config.ctrl.abort()
      } catch (e) {
        console.error(e)
      }
    }

  }

  params(param) {
    if (param == null || param == "") {
      return new URLSearchParams();
    }
    if (param.constructor == Array) {
      let param1 = new URLSearchParams();
      for (let obj of param) {
        param1.append(obj.name, obj.value);
      }
      param = param1;
    } else if (param.constructor == Object) {
      let param1 = new URLSearchParams();
      for (let name in param) {
        param1.append(name, param[name]);
      }
      param = param1;
    } else {
      if (param.constructor == HTMLFormElement) {
        param = new FormData(param);
      }
      if (param.constructor == FormData || param.constructor == String) {
        param = new URLSearchParams(param);
      }
    }

    return param;
  }

}

export default ServerSentEvents
