import os from "os"
import fs from "fs"
import { join, sep } from "path"
import isDocker from "is-docker"
import { v4 as uuidv4 } from "uuid"

import createFlush from "./util/create-flush"
import getTermProgram from "./util/get-term-program"
import isTruthy from "./util/is-truthy"
import showAnalyticsNotification from "./util/show-notification"
import { isCI, getCIName } from "./util/is-ci"
import Store from "./store"

const MEDUSA_TELEMETRY_VERBOSE = process.env.MEDUSA_TELEMETRY_VERBOSE || false

class Telemeter {
  constructor(options = {}) {
    this.store_ = new Store()

    this.flushAt = Math.max(options.flushAt, 1) || 20
    this.maxQueueSize = options.maxQueueSize || 1024 * 500
    this.flushInterval = options.flushInterval || 10 * 1000
    this.flushed = false

    this.queueSize_ = this.store_.getQueueSize()
    this.queueCount_ = this.store_.getQueueCount()
  }

  getMachineId() {
    if (this.machineId) {
      return this.machineId
    }
    let machineId = this.store_.getConfig(`telemetry.machine_id`)
    if (typeof machineId !== `string`) {
      machineId = uuidv4()
      this.store_.setConfig(`telemetry.machine_id`, machineId)
    }
    this.machineId = machineId
    return machineId
  }

  isTrackingEnabled() {
    // Cache the result
    if (this.trackingEnabled !== undefined) {
      return this.trackingEnabled
    }
    let enabled = this.store_.getConfig(`telemetry.enabled`)
    if (enabled === undefined || enabled === null) {
      if (!isCI()) {
        showAnalyticsNotification()
      }
      enabled = true
      this.store_.setConfig(`telemetry.enabled`, enabled)
    }
    this.trackingEnabled = enabled
    return enabled
  }

  getOsInfo() {
    if (this.osInfo) {
      return this.osInfo
    }
    const cpus = os.cpus()
    const osInfo = {
      node_version: process.version,
      platform: os.platform(),
      release: os.release(),
      cpus: (cpus && cpus.length > 0 && cpus[0].model) || undefined,
      is_ci: isCI(),
      ci_name: getCIName(),
      arch: os.arch(),
      docker: isDocker(),
      term_program: getTermProgram(),
    }
    this.osInfo = osInfo
    return osInfo
  }

  getMedusaVersion() {
    try {
      const packageJson = require.resolve(`@medusajs/medusa/package.json`)
      const { version } = JSON.parse(fs.readFileSync(packageJson, `utf-8`))
      return version
    } catch (e) {
      if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
        console.error("failed to get medusa version", e)
      }
    }
    return `-0.0.0`
  }

  getCliVersion() {
    try {
      const jsonfile = join(
        require
          .resolve(`@medusajs/medusa-cli`) // Resolve where current gatsby-cli would be loaded from.
          .split(sep)
          .slice(0, -2) // drop lib/index.js
          .join(sep),
        `package.json`
      )

      const { version } = require(jsonfile)
      return version
    } catch (e) {
      if (isTruthy(MEDUSA_TELEMETRY_VERBOSE)) {
        console.error("failed to get medusa version", e)
      }
    }

    return `-0.0.0`
  }

  setTelemetryEnabled(enabled) {
    this.trackingEnabled = enabled
    this.store_.setConfig(`telemetry.enabled`, enabled)
  }

  track(event, data) {
    return this.enqueue_(event, data)
  }

  enqueue_(type, data) {
    const event = {
      id: `te_${uuidv4()}`,
      type,
      properties: data,
      timestamp: new Date(),
      machine_id: this.getMachineId(),
      os_info: this.getOsInfo(),
      medusa_version: this.getMedusaVersion(),
      cli_version: this.getCliVersion(),
    }

    this.store_.addEvent(event)

    this.queueCount_ += 1
    this.queueSize_ += JSON.stringify(event).length

    const hasReachedFlushAt = this.queueCount_ >= this.flushAt
    const hasReachedQueueSize = this.queueSize_ >= this.maxQueueSize

    if (hasReachedQueueSize || hasReachedFlushAt) {
      const flush = createFlush(this.isTrackingEnabled())
      flush && flush()
    }

    if (this.flushInterval && !this.timer) {
      const flush = createFlush(this.isTrackingEnabled())
      if (flush) {
        this.timer = setTimeout(flush, this.flushInterval)
      }
    }
  }
}

export default Telemeter
