import {
  Service,
  PlatformAccessory,
  CharacteristicGetCallback,
} from "homebridge";

import { GoveeHomebridgePlatform } from "./platform";
import { GoveeReading } from "govee-bt-client";
import { DeviceContext } from "./deviceContext";

/**
 * Platform Accessory
 * An instance of this class is created for each accessory your platform registers
 * Each accessory may expose multiple services of different service types.
 */
export class GoveePlatformAccessory {
  private LOW_BATTERY_THRESHOLD = 25;
  private HUMIDITY_OFFSET = 0;

  private humiditySensor: Service;
  private temperatureSensor: Service;
  private lastReading: GoveeReading;

  constructor(
    private readonly platform: GoveeHomebridgePlatform,
    private readonly accessory: PlatformAccessory,
    private readonly reading: GoveeReading
  ) {
    this.lastReading = reading;

    if (!accessory.context.device) {
      throw new Error("Missing device context");
    }

    const deviceContext: DeviceContext = accessory.context.device;

    if (accessory.context.batteryThreshold) {
      this.LOW_BATTERY_THRESHOLD = accessory.context.batteryThreshold;
    }
    if (accessory.context.humidityOffset) {
      this.HUMIDITY_OFFSET = accessory.context.humidityOffset;
    }
    // set accessory information
    const accessoryInformationService = this.accessory.getService(
      this.platform.Service.AccessoryInformation
    );

    accessoryInformationService
      ?.setCharacteristic(this.platform.Characteristic.Manufacturer, "Govee")
      .setCharacteristic(
        this.platform.Characteristic.Model,
        deviceContext.model
      );

    if (deviceContext.address && deviceContext.address !== "") {
      accessoryInformationService?.setCharacteristic(
        this.platform.Characteristic.SerialNumber,
        deviceContext.address
      );
    }

    // get the HumiditySensor service if it exists, otherwise create a new HumiditySensor service
    // you can create multiple services for each accessory
    this.humiditySensor =
      this.accessory.getService(this.platform.Service.HumiditySensor) ||
      this.accessory.addService(this.platform.Service.HumiditySensor);
    this.humiditySensor.setCharacteristic(
      this.platform.Characteristic.Name,
      deviceContext.model
    );
    // each service must implement at-minimum the "required characteristics" for the given service type
    // see https://developers.homebridge.io/#/service/Lightbulb

    this.humiditySensor
      .getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity)
      .on("get", this.getCurrentRelativeHumidity.bind(this));
    this.humiditySensor
      .getCharacteristic(this.platform.Characteristic.StatusLowBattery)
      .on("get", this.getStatusLowBattery.bind(this));

    this.temperatureSensor =
      this.accessory.getService(this.platform.Service.TemperatureSensor) ||
      this.accessory.addService(this.platform.Service.TemperatureSensor);

    this.temperatureSensor
      .getCharacteristic(this.platform.Characteristic.CurrentTemperature)
      .on("get", this.getCurrentTemperature.bind(this));
    this.temperatureSensor
      .getCharacteristic(this.platform.Characteristic.StatusLowBattery)
      .on("get", this.getStatusLowBattery.bind(this));
  }

  getCurrentRelativeHumidity(callback: CharacteristicGetCallback) {
    this.platform.log.debug(
      "getCurrentRelativeHumidity",
      this.lastReading?.humidity,
      "offset",
      this.HUMIDITY_OFFSET
    );
    callback(null, this.lastReading?.humidity + this.HUMIDITY_OFFSET);
  }

  getStatusLowBattery(callback: CharacteristicGetCallback) {
    this.platform.log.debug("getStatusLowBattery");
    callback(null, this.lastReading?.battery <= this.LOW_BATTERY_THRESHOLD);
  }

  getCurrentTemperature(callback: CharacteristicGetCallback) {
    this.platform.log.debug("getCurrentTemperature", this.lastReading?.tempInC);
    callback(null, this.lastReading?.tempInC);
  }

  updateReading(reading: GoveeReading) {
    this.lastReading = reading;

    this.humiditySensor.updateCharacteristic(
      this.platform.Characteristic.CurrentRelativeHumidity,
      reading.humidity + this.HUMIDITY_OFFSET
    );
    this.temperatureSensor.updateCharacteristic(
      this.platform.Characteristic.CurrentTemperature,
      reading.tempInC
    );
  }
}