// vim: set ts=2 sw=2 et tw=80:

import axios from "axios";

let config = window.BACKEND_URL || "http://localhost:8080/";
var tkn = localStorage.getItem("token");

/** the ServiceSocket instance valid for the current session */
var socket;

// requests data devices
/*

    {
  params : data,
  device: 'tipoDiDevice',
  id: se serve
    }


    device routes:
  - buttonDimmer
  - dimmableLight
  - knobDimmer
  - motionSensor
  - regularLight
  - sensor
  - smartPlug
  - switch

*/

/** The number of times a connection to the socket was tried */
var retries = 0;

/** Class to handle connection with the sensor socket */
class ServiceSocket {
  /**
   * Create a new sensor socket connection
   * @param {string} token - The JWT token (needed for authentication)
   * @param {Object.<number, function>|null} callbacks - A callback map from
   *  device id to callback function
   */
  constructor(token, callbacks) {
    this.token = token;
    this.authenticated = false;
    this.callbacks = callbacks || {};

    this.connection = new WebSocket("ws://localhost:8080/sensor-socket");

    this.connection.onopen = (evt) => {
      this.connection.send(JSON.stringify({ token }));
    };

    this.connection.onmessage = (evt) => {
      let data = JSON.parse(evt.data);

      if (!this.authenticated) {
        if (data.authenticated) {
          this.authenticated = true;
          retries = 0;
        } else {
          console.error("socket authentication failed");
        }
      } else {
        this.invokeCallbacks(data);
      }
    };

    this.connection.onerror = (evt) => {
      if (retries >= 5) {
        console.error("too many socket connection retries");
        return;
      }
      retries++;
      socket = new ServiceSocket(this.token, this.callbacks);
    };
  }

  invokeCallbacks(data) {
    if (data.id && this.callbacks[data.id]) {
      this.callbacks[data.id].forEach((f) => f(data));
    }
  }

  /**
   * Registers a new callback function to be called when updates on the device
   * with the id given are recieved
   * @param {number} id - the id of the device to check updates for
   * @param {function} stateCallback - a function that recieves a device as the
   *  first parameter, that will be called whenever a update is recieved
   */
  subscribe(id, stateCallback) {
    if (this.callbacks[id] === undefined) {
      this.callbacks[id] = [];
    }

    this.callbacks[id].push(stateCallback);
  }

  /**
   * Unregisters a function previously registered with `subscribe(...)`.
   * @param {number} id - the id of the device to stop checking updates for
   * @param {function} stateCallback - the callback to unregister
   */
  unsubscribe(id, stateCallback) {
    this.callbacks[id].splice(this.callbacks[id].indexOf(stateCallback), 1);
  }

  /**
   * Closes the underlying websocket connection
   */
  close() {
    this.connection.close();
  }
}

if (tkn) {
  socket = new ServiceSocket(tkn);
}

export var call = {
  setToken: function (token) {
    tkn = token;
    if (tkn) {
      if (socket) {
        socket.close();
      }
      socket = new ServiceSocket(tkn);
    }
  },

  /**
   * Registers a new callback function to be called when updates on the device
   * with the id given are recieved
   * @param {number} id - the id of the device to check updates for
   * @param {function} stateCallback - a function that recieves a device as the
   *  first parameter, that will be called whenever a update is recieved
   */
  socketSubscribe: function (id, callback) {
    socket.subscribe(id, callback);
  },

  /**
   * Unregisters a function previously registered with `subscribe(...)`.
   * @param {number} id - the id of the device to stop checking updates for
   * @param {function} stateCallback - the callback to unregister
   */
  socketUnsubscribe: function (id, callback) {
    socket.unsubscribe(id, callback);
  },
  login: function (data, headers) {
    return axios.post(config + "auth/login", data);
  },
  register: function (data, headers) {
    return axios.post(config + "register", data);
  },
  initResetPassword: function (data, headers) {
    return axios.post(config + "register/init-reset-password", data);
  },
  resetPassword: function (data, headers) {
    return axios.put(config + "register/reset-password", data);
  },
  getAllRooms: function (token) {
    if (!token) {
      token = tkn;
    }
    return axios.get(config + "room", {
      headers: { Authorization: "Bearer " + token },
    });
  },
  getAllDevices: function (token) {
    if (!token) {
      token = tkn;
    }
    return axios.get(config + "device", {
      headers: { Authorization: "Bearer " + token },
    });
  },
  getAllDevicesByRoom: function (id, token) {
    if (!token) {
      token = tkn;
    }
    return axios.get(config + "room/" + id + "/devices", {
      headers: { Authorization: "Bearer " + token },
    });
  },
  createRoom: function (data, headers) {
    return axios.post(config + "room", data, {
      headers: { Authorization: "Bearer " + tkn },
    });
  },
  updateRoom: function (data, headers) {
    return axios.put(config + "room/" + data.id, data, {
      headers: { Authorization: "Bearer " + tkn },
    });
  },
  deleteRoom: function (data, headers) {
    return axios.delete(config + "room/" + data.id, {
      headers: { Authorization: "Bearer " + tkn },
    });
  },
  devicePost: function (data, headers) {
    return axios
      .post(config + data.device, data.params, {
        headers: { Authorization: "Bearer " + tkn },
      })
      .then((res) => {
        if (res.status === 200 && data.device === "switch") {
          data.params.lights.forEach((e) => {
            let urlUp =
              config +
              data.device +
              "/" +
              res.data.id +
              "/lights?switchableId=" +
              e;
            axios.post(
              urlUp,
              {},
              { headers: { Authorization: "Bearer " + tkn } }
            );
          });
        }
        return res;
      });
  },
  deviceUpdate: function (data, typeDevice) {
    let url = "device";
    if (typeDevice) {
      url = typeDevice;
    }
    let promiseRes = axios.put(config + url, data, {
      headers: { Authorization: "Bearer " + tkn },
    });
    // also for btn/knob dimmer
    if (typeDevice === "switch/operate") {
      promiseRes = promiseRes.then((e) => {
        if (e.status === 200) {
          e.data.forEach((device) => socket.invokeCallbacks(device));
        }
        return e;
      });
    }

    return promiseRes;
  },
  deviceDelete: function (data, headers) {
    return axios.delete(config + data.device + "/" + data.id, {
      headers: { Authorization: "Bearer " + tkn },
    });
  },
  deviceGetById: function (data, headers) {
    return axios.get(config + data.device + "/" + data.id);
  },
  deviceGetAll: function (data, headers) {
    return axios.get(config + data.device);
  },
};
