import { Injectable } from '@angular/core';
import { v4 as uuidv4 } from 'uuid'
import { wsCallbacksParams, msgQueue } from 'src/app/models/rochet-chat-cliente-service'

class _RocketChatClientService {

  private hasPing = false
  private firstPingFunx: Function
  isLogin = false;
  requestQueue = []
  
  constructor() {}

  private returns() {}
  
  private attemptRequestQueue(arg, option) {

    const conditionToSend = this.ws.connected == true && this.isLogin == true

    if(conditionToSend) {
      this.requestQueue.push({
        arg: arg,
        status: {
          isLogin: this.isLogin,
          isConnected: this.ws.connected
        }
      })
    }
    return conditionToSend
  }

  private runRequestQueue(){}

  connect(firstPingFunx: Function) {
    this.hasPing = false
    this.firstPingFunx = firstPingFunx

    this.ws.connect('wss://gabinetedigitalchat.dyndns.info/websocket');

    const connectMessage = {
      msg: "connect",
      version: "1",
      support: ["1"]
    }

    this.ws.send(connectMessage)
    
    this.ws.registerCallback({type:'Onmessage',funx:(message: any) => {

      if(message.msg == "ping") {
        this.ws.send({msg:"pong"})
        if(this.hasPing == false) {
          // first ping
          firstPingFunx()
          this.hasPing = true
        }
      }

    }})

  }

  login(user) {

    const requestId =  uuidv4()
    this.isLogin = false

    const loginRequest = {
      msg: "method",
      method: "login",
      id: requestId,
      params: [
        {
          user: { username: user.username },
          password: user.password
        }
      ]
    }
    this.ws.send(loginRequest, requestId)

    return new Promise((resolve, reject) => {
      this.ws.registerCallback({type:'Onmessage', requestId, runOnces: true, funx:(message)=>{
        // console.log(message.result.id, requestId)
        if(message.id == requestId || message.result.id == requestId) { // same request send
          if(message.result) {
            if(message.result.token) {
              this.isLogin = true
              this.runRequestQueue()
              this.ws.wsMsgQueue()
              resolve(message)
            } else {
              this.isLogin = false
              reject(message)
            }
          } else {
            this.isLogin = false
            reject(message)
          }
        }
      }})
    });

  }

  logout(){}

  send(roomId, message) {

    const conditions = this.attemptRequestQueue({...roomId, message}, {methodName:'send'})
    if(conditions == false) { return new Promise((resolve, reject) => { reject('no error')}); }

    const requestId =  uuidv4()

    var request = {
      msg: "method",
      method: "sendMessage",
      id: requestId,
      params: [{
        _id: uuidv4(),
        rid: roomId,
        msg: message
      }]
    }

    this.ws.send(request, requestId);

    return new Promise((resolve, reject) => {
      this.ws.registerCallback({type:'Onmessage', requestId, runOnces: true, funx:(message)=>{
        if(message.id == requestId || message.result.id == requestId) { // same request send
          resolve(message)
        }
      }})
    });
  }

  joinRoom(){}
  deleteMessage() {}
  createRoom() {}

  getRooms() {
    const conditions = this.attemptRequestQueue({},{methodName: 'getRooms'})
    if(conditions == false) { return new Promise((resolve, reject) => { reject('no error')}); }

    const requestId =  uuidv4()

    const request = {
      "msg": "method",
      "method": "rooms/get",
      "id": requestId,
      "params": [ { "$date": 1480377601 } ]
    }
    this.ws.send(request, requestId)

    return new Promise((resolve, reject) => {
      this.ws.registerCallback({type:'Onmessage', requestId, runOnces: true, funx:(message)=>{
        if(message.id == requestId || message.result.id == requestId) { // same request send
          resolve(message)
        }
      }})
    });
  }


  subscribe(roomId : string) {

    const conditions = this.attemptRequestQueue({roomId},{methodName: 'subscribe'})
    if(conditions == false) { return new Promise((resolve, reject) => { reject('no error')}); }

    const requestId =  uuidv4()
    var subscribeRequest = {
      "msg": "sub",
      "id": requestId,
      "name": "stream-notify-room",
      "params":[
      `${roomId}/event`,
        false
      ]
    }

    this.ws.send(subscribeRequest);

    return new Promise((resolve, reject) => {
      this.ws.registerCallback({type:'Onmessage', requestId, runOnces: true, funx:(message)=>{
        if(message.result.id == requestId) { // same request send
          resolve(message)
        }
      }})
    });

  }

  private disconnect = () => {

  }
  
  // socket class ==================================================================
  private socket!: WebSocket;
  private wsUrl = ''
  private wsMsgQueue : msgQueue[] = []
  private wsCallbacks: {[key: string]:  wsCallbacksParams} = {}

  private ws = {
    connected: false,
    registerCallback:(params: wsCallbacksParams) =>{
      
      if(!params.requestId) {
        params.requestId = uuidv4()
      }

      this.wsCallbacks[params.requestId] = {
        type: params.type,
        funx: params.funx,
        runOnces: params.runOnces
      }

    },
    deleteCallback(){},
    connect:(url)=> {
      this.ws.connected = false
      this.wsUrl = url
      this.socket = new WebSocket(this.wsUrl);
      // bind function
      this.socket.onopen = this.ws.onopen;
      this.socket.onmessage = this.ws.onmessage;
      this.socket.onclose = this.ws.onclose;
      this.socket.onerror = this.ws.onerror;
    },
    onopen:()=> {
      this.ws.connected = true
      console.log('================== welcome to socket server =====================')
      this.wsMsgQueue.forEach((item, index, object) => {
        this.ws.send(item.message, item.requestId);
        object.splice(index, 1);
      })
    },
    wsMsgQueue:()=> {
      // this.wsMsgQueue.forEach((item, index, object) => {
      //   this.ws.send(item.message, item.requestId);
      //   object.splice(index, 1);
      // })
    },
    send: (message: object, requestId = uuidv4()) => {
  
      if (this.ws.connected == false) { // save data to send when back online
        console.log('save msgQueue')
        this.wsMsgQueue.push({message, requestId})
      } else {
        console.log('send to rocket chat', message)
        let messageStr = JSON.stringify(message)
        this.socket.send(messageStr)
      }
      return requestId
    },
  
    onmessage:(event: any)=> {
      const data = JSON.parse(event.data)
      console.log('event.data', data)
  
      for (const [key, value] of Object.entries(this.wsCallbacks)) {
        if(value.type== 'Onmessage') {
          value.funx(data)
          if(value.runOnces) {
            delete this.wsCallbacks[value.requestId || key]
          } 
        }


      }

    },
    
    onclose:(event: any)=> {

      this.connect(this.firstPingFunx())

      this.ws.connected = false
      console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
    },
  
    onerror: (event: any) =>  {
      console.log(`[error] ${event.message}`);
    }

  }

}


export const RocketChatClientService = new _RocketChatClientService()