import mqtt from 'mqtt'

export default class MQTTService {
  /**
   * @param {string} brokerUrl - MQTT broker URL (e.g., ws://127.0.0.1:8083/mqtt)
   * @param {string|null} clientId - Optional MQTT client ID
   * @param {string|null} username - Optional MQTT username
   * @param {string|null} password - Optional MQTT password
   */
  constructor(
    brokerUrl = 'ws://127.0.0.1:8083/mqtt',
    username = null,
    password = null,
    clientId = null,
  ) {
    this.clientId = clientId || 'vue-client-' + Math.random().toString(16).substr(2, 8)

    // Connect options
    const options = {
      clientId: this.clientId,
      protocolVersion: 5,
    }
    if (username) options.username = username
    if (password) options.password = password

    this.client = mqtt.connect(brokerUrl, options)
    this.subscriptions = [] // array of { topic, callback }

    this.client.on('connect', () => {
      console.log(`Connected to MQTT broker as ${this.clientId}`)
    })
    this.client.on('message', (topic, payload) => {
      const message = payload.toString()
      this.subscriptions.forEach(({ topic: subTopic, callback }) => {
        if (mqttMatch(subTopic, topic)) {
          callback(message, topic)
        }
      })
    })
  }

  /**
   * Subscribe to a topic
   * @param {string} topic
   * @param {function} callback
   */
  subscribe(topic, callback) {
    this.subscriptions.push({ topic, callback })
    this.client.subscribe(topic)
  }

  /**
   * Unsubscribe from a topic
   * @param {string} topic
   * @param {function|null} callback - optional, remove only this callback
   */
  unsubscribe(topic, callback = null) {
    this.subscriptions = this.subscriptions.filter((sub) => {
      if (sub.topic !== topic) return true
      if (callback && sub.callback !== callback) return true
      return false
    })

    // Actually tell the broker to unsubscribe only if no more callbacks exist for this topic
    const stillSubscribed = this.subscriptions.some((sub) => sub.topic === topic)
    if (!stillSubscribed) {
      this.client.unsubscribe(topic)
    }
  }

  /**
   * Publish a message
   * @param {string} topic
   * @param {string|Buffer} message
   * @param {object} options
   */
  publish(topic, message, options = {}) {
    this.client.publish(topic, message, options)
  }
}

// helper function for MQTT wildcards
function mqttMatch(subTopic, topic) {
  // replace MQTT wildcards with RegExp
  const regex = '^' + subTopic.replace(/\+/g, '[^/]+').replace(/#/g, '.+') + '$'
  return new RegExp(regex).test(topic)
}
