/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
 *
 * airpurifier.ts: @switchbot/homebridge-switchbot.
 */
import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'
import type { bodyChange, irdevice } from 'node-switchbot'

import type { SwitchBotPlatform } from '../platform.js'
import type { irDevicesConfig } from '../settings.js'

import { irdeviceBase } from './irdevice.js'

/**
 * 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 AirPurifier extends irdeviceBase {
  // Services
  private AirPurifier: {
    Name: CharacteristicValue
    Service: Service
    Active: CharacteristicValue
    RotationSpeed: CharacteristicValue
    CurrentAirPurifierState: CharacteristicValue
    TargetAirPurifierState: CharacteristicValue
    CurrentHeaterCoolerState: CharacteristicValue
  }

  private TemperatureSensor: {
    Name: CharacteristicValue
    Service: Service
    CurrentTemperature: CharacteristicValue
  }

  // Characteristic Values
  APActive!: CharacteristicValue
  CurrentAPTemp!: CharacteristicValue
  CurrentAPMode!: CharacteristicValue
  CurrentAPFanSpeed!: CharacteristicValue

  // Others
  Busy: any
  Timeout: any = null
  static IDLE: number
  CurrentMode!: number
  static INACTIVE: number
  LastTemperature!: number
  CurrentFanSpeed!: number
  static PURIFYING_AIR: number

  constructor(
    readonly platform: SwitchBotPlatform,
    accessory: PlatformAccessory,
    device: irdevice & irDevicesConfig,
  ) {
    super(platform, accessory, device)
    // Set category
    accessory.category = this.hap.Categories.AIR_PURIFIER

    // Initialize AirPurifier Service
    accessory.context.AirPurifier = accessory.context.AirPurifier ?? {}
    this.AirPurifier = {
      Name: accessory.displayName,
      Service: accessory.getService(this.hap.Service.AirPurifier) ?? accessory.addService(this.hap.Service.AirPurifier) as Service,
      Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE,
      RotationSpeed: accessory.context.RotationSpeed ?? 0,
      CurrentAirPurifierState: accessory.context.CurrentAirPurifierState ?? this.hap.Characteristic.CurrentAirPurifierState.INACTIVE,
      TargetAirPurifierState: accessory.context.TargetAirPurifierState ?? this.hap.Characteristic.TargetAirPurifierState.AUTO,
      CurrentHeaterCoolerState: accessory.context.CurrentHeaterCoolerState ?? this.hap.Characteristic.CurrentHeaterCoolerState.INACTIVE,
    }
    accessory.context.AirPurifier = this.AirPurifier as object

    this.AirPurifier.Service.setCharacteristic(this.hap.Characteristic.Name, this.AirPurifier.Name).getCharacteristic(this.hap.Characteristic.Active).onGet(() => {
      return this.AirPurifier.Active
    }).onSet(this.ActiveSet.bind(this))

    this.AirPurifier.Service.getCharacteristic(this.hap.Characteristic.CurrentAirPurifierState).onGet(() => {
      return this.CurrentAirPurifierStateGet()
    })

    this.AirPurifier.Service.getCharacteristic(this.hap.Characteristic.TargetAirPurifierState).onGet(() => {
      return this.AirPurifier.TargetAirPurifierState
    }).onSet(this.TargetAirPurifierStateSet.bind(this))

    // Initialize TemperatureSensor Service
    accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {}
    this.TemperatureSensor = {
      Name: `${accessory.displayName} Temperature Sensor`,
      Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? accessory.addService(this.hap.Service.TemperatureSensor) as Service,
      CurrentTemperature: accessory.context.CurrentTemperature || 24,
    }
    accessory.context.TemperatureSensor = this.TemperatureSensor as object

    this.TemperatureSensor.Service.setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name).getCharacteristic(this.hap.Characteristic.CurrentTemperature).onGet(() => {
      return this.TemperatureSensor.CurrentTemperature
    })
  }

  async ActiveSet(value: CharacteristicValue): Promise<void> {
    this.debugLog(`Set Active: ${value}`)

    this.AirPurifier.Active = value
    if (this.AirPurifier.Active === this.hap.Characteristic.Active.ACTIVE) {
      this.pushAirPurifierOnChanges()
    } else {
      this.pushAirPurifierOffChanges()
    }
  }

  async TargetAirPurifierStateSet(value: CharacteristicValue): Promise<void> {
    switch (value) {
      case this.hap.Characteristic.CurrentAirPurifierState.PURIFYING_AIR:
        this.CurrentMode = AirPurifier.PURIFYING_AIR
        break
      case this.hap.Characteristic.CurrentAirPurifierState.IDLE:
        this.CurrentMode = AirPurifier.IDLE
        break
      case this.hap.Characteristic.CurrentAirPurifierState.INACTIVE:
        this.CurrentMode = AirPurifier.INACTIVE
        break
      default:
        break
    }
  }

  async CurrentAirPurifierStateGet(): Promise<number> {
    if (this.AirPurifier.Active === 1) {
      this.AirPurifier.CurrentAirPurifierState = this.hap.Characteristic.CurrentAirPurifierState.PURIFYING_AIR
    } else {
      this.AirPurifier.CurrentAirPurifierState = this.hap.Characteristic.CurrentAirPurifierState.INACTIVE
    }
    return this.AirPurifier.CurrentAirPurifierState
  }

  /**
   * Pushes the requested changes to the SwitchBot API
   * deviceType          commandType     Command          command parameter          Description
   * AirPurifier:        "command"       "turnOn"         "default"         =        every home appliance can be turned on by default
   * AirPurifier:        "command"       "turnOff"        "default"         =        every home appliance can be turned off by default
   * AirPurifier:        "command"       "swing"          "default"         =        swing
   * AirPurifier:        "command"       "timer"          "default"         =        timer
   * AirPurifier:        "command"       "lowSpeed"       "default"         =        fan speed to low
   * AirPurifier:        "command"       "middleSpeed"    "default"         =        fan speed to medium
   * AirPurifier:        "command"       "highSpeed"      "default"         =        fan speed to high
   */
  async pushAirPurifierOnChanges(): Promise<void> {
    this.debugLog(`pushAirPurifierOnChanges Active: ${this.AirPurifier.Active}, disablePushOn: ${this.deviceDisablePushOn}`)
    if (this.AirPurifier.Active === this.hap.Characteristic.Active.ACTIVE && !this.deviceDisablePushOn) {
      const commandType: string = await this.commandType()
      const command: string = await this.commandOn()
      const bodyChange: bodyChange = {
        command,
        parameter: 'default',
        commandType,
      }
      await this.pushChanges(bodyChange)
    }
  }

  async pushAirPurifierOffChanges(): Promise<void> {
    this.debugLog(`pushAirPurifierOffChanges Active: ${this.AirPurifier.Active}, disablePushOff: ${this.deviceDisablePushOff}`)
    if (this.AirPurifier.Active === this.hap.Characteristic.Active.INACTIVE && !this.deviceDisablePushOn) {
      const commandType: string = await this.commandType()
      const command: string = await this.commandOff()
      const bodyChange: bodyChange = {
        command,
        parameter: 'default',
        commandType,
      }
      await this.pushChanges(bodyChange)
    }
  }

  async pushAirPurifierStatusChanges(): Promise<void> {
    this.debugLog(`pushAirPurifierStatusChanges Active: ${this.AirPurifier.Active}, disablePushOff: ${this.deviceDisablePushOff}, disablePushOn: ${this.deviceDisablePushOn}`)
    if (!this.Busy) {
      this.Busy = true
      this.AirPurifier.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.IDLE
    }
    clearTimeout(this.Timeout)

    // Make a new Timeout set to go off in 1000ms (1 second)
    this.Timeout = setTimeout(this.pushAirPurifierDetailsChanges.bind(this), 1500)
  }

  async pushAirPurifierDetailsChanges(): Promise<void> {
    this.debugLog(`pushAirPurifierDetailsChanges Active: ${this.AirPurifier.Active}, disablePushOff: ${this.deviceDisablePushOff}, disablePushOn: ${this.deviceDisablePushOn}`)
    this.CurrentAPTemp = this.TemperatureSensor!.CurrentTemperature ?? 24
    this.CurrentAPMode = this.CurrentMode ?? 1
    this.CurrentAPFanSpeed = this.CurrentFanSpeed ?? 1
    this.APActive = this.AirPurifier.Active === 1 ? 'on' : 'off'
    const parameter = `${this.CurrentAPTemp},${this.CurrentAPMode},${this.CurrentAPFanSpeed},${this.APActive}`
    const bodyChange: bodyChange = {
      command: 'setAll',
      parameter: `${parameter}`,
      commandType: 'command',
    }
    if (this.AirPurifier.Active === 1) {
      if ((Number(this.TemperatureSensor!.CurrentTemperature) || 24) < (this.LastTemperature || 30)) {
        this.AirPurifier.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.COOLING
      } else {
        this.AirPurifier.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.HEATING
      }
    } else {
      this.AirPurifier.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.INACTIVE
    }
    await this.pushChanges(bodyChange)
  }

  async pushChanges(bodyChange: any): Promise<void> {
    this.debugLog('pushChanges')
    if (this.device.connectionType === 'OpenAPI') {
      this.infoLog(`Sending request to SwitchBot API, body: ${JSON.stringify(bodyChange)}`)
      try {
        const response = await this.pushChangeRequest(bodyChange)
        const deviceStatus: any = response.body
        await this.pushStatusCodes(deviceStatus)
        if (await this.successfulStatusCodes(deviceStatus)) {
          await this.successfulPushChange(deviceStatus, bodyChange)
          await this.updateHomeKitCharacteristics()
        } else {
          await this.statusCode(deviceStatus.statusCode)
        }
      } catch (e: any) {
        await this.apiError(e)
        await this.pushChangeError(e)
      }
    } else {
      this.warnLog(`Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`)
    }
  }

  async updateHomeKitCharacteristics(): Promise<void> {
    this.debugLog('updateHomeKitCharacteristics')
    // Active
    await this.updateCharacteristic(this.AirPurifier.Service, this.hap.Characteristic.Active, this.AirPurifier.Active, 'Active')
    // CurrentAirPurifierState
    await this.updateCharacteristic(this.AirPurifier.Service, this.hap.Characteristic.CurrentAirPurifierState, this.AirPurifier.CurrentAirPurifierState, 'CurrentAirPurifierState')
    // CurrentHeaterCoolerState
    await this.updateCharacteristic(this.AirPurifier.Service, this.hap.Characteristic.CurrentHeaterCoolerState, this.AirPurifier.CurrentHeaterCoolerState, 'CurrentHeaterCoolerState')
    // CurrentTemperature
    await this.updateCharacteristic(this.TemperatureSensor.Service, this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor.CurrentTemperature, 'CurrentTemperature')
  }

  async apiError(e: any): Promise<void> {
    this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.Active, e)
    this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.TargetAirPurifierState, e)
    this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentAirPurifierState, e)
    this.AirPurifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHeaterCoolerState, e)
    this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e)
  }
}
