// SPDX-License-Identifier: GPL-3.0-or-later

package upsd

import (
	"fmt"
	"strings"

	"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
)

const (
	prioUpsLoad = module.Priority + iota
	prioUpsLoadUsage
	prioUpsStatus
	prioUpsTemperature

	prioBatteryCharge
	prioBatteryEstimatedRuntime
	prioBatteryVoltage
	prioBatteryVoltageNominal

	prioInputVoltage
	prioInputVoltageNominal
	prioInputCurrent
	prioInputCurrentNominal
	prioInputFrequency
	prioInputFrequencyNominal

	prioOutputVoltage
	prioOutputVoltageNominal
	prioOutputCurrent
	prioOutputCurrentNominal
	prioOutputFrequency
	prioOutputFrequencyNominal
)

var upsChartsTmpl = module.Charts{
	upsLoadChartTmpl.Copy(),
	upsLoadUsageChartTmpl.Copy(),
	upsStatusChartTmpl.Copy(),
	upsTemperatureChartTmpl.Copy(),

	upsBatteryChargePercentChartTmpl.Copy(),
	upsBatteryEstimatedRuntimeChartTmpl.Copy(),
	upsBatteryVoltageChartTmpl.Copy(),
	upsBatteryVoltageNominalChartTmpl.Copy(),

	upsInputVoltageChartTmpl.Copy(),
	upsInputVoltageNominalChartTmpl.Copy(),
	upsInputCurrentChartTmpl.Copy(),
	upsInputCurrentNominalChartTmpl.Copy(),
	upsInputFrequencyChartTmpl.Copy(),
	upsInputFrequencyNominalChartTmpl.Copy(),

	upsOutputVoltageChartTmpl.Copy(),
	upsOutputVoltageNominalChartTmpl.Copy(),
	upsOutputCurrentChartTmpl.Copy(),
	upsOutputCurrentNominalChartTmpl.Copy(),
	upsOutputFrequencyChartTmpl.Copy(),
	upsOutputFrequencyNominalChartTmpl.Copy(),
}

var (
	upsLoadChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.load_percentage",
		Title:    "UPS load",
		Units:    "percentage",
		Fam:      "ups",
		Ctx:      "upsd.ups_load",
		Priority: prioUpsLoad,
		Type:     module.Area,
		Dims: module.Dims{
			{ID: "ups_%s_ups.load", Name: "load", Div: varPrecision},
		},
	}
	upsLoadUsageChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.load_usage",
		Title:    "UPS load usage (power output)",
		Units:    "Watts",
		Fam:      "ups",
		Ctx:      "upsd.ups_load_usage",
		Priority: prioUpsLoadUsage,
		Dims: module.Dims{
			{ID: "ups_%s_ups.load.usage", Name: "load_usage", Div: varPrecision},
		},
	}
	upsStatusChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.status",
		Title:    "UPS status",
		Units:    "status",
		Fam:      "ups",
		Ctx:      "upsd.ups_status",
		Priority: prioUpsStatus,
		Dims: module.Dims{
			{ID: "ups_%s_ups.status.OL", Name: "on_line"},
			{ID: "ups_%s_ups.status.OB", Name: "on_battery"},
			{ID: "ups_%s_ups.status.LB", Name: "low_battery"},
			{ID: "ups_%s_ups.status.HB", Name: "high_battery"},
			{ID: "ups_%s_ups.status.RB", Name: "replace_battery"},
			{ID: "ups_%s_ups.status.CHRG", Name: "charging"},
			{ID: "ups_%s_ups.status.DISCHRG", Name: "discharging"},
			{ID: "ups_%s_ups.status.BYPASS", Name: "bypass"},
			{ID: "ups_%s_ups.status.CAL", Name: "calibration"},
			{ID: "ups_%s_ups.status.OFF", Name: "offline"},
			{ID: "ups_%s_ups.status.OVER", Name: "overloaded"},
			{ID: "ups_%s_ups.status.TRIM", Name: "trim_input_voltage"},
			{ID: "ups_%s_ups.status.BOOST", Name: "boost_input_voltage"},
			{ID: "ups_%s_ups.status.FSD", Name: "forced_shutdown"},
			{ID: "ups_%s_ups.status.other", Name: "other"},
		},
	}
	upsTemperatureChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.temperature",
		Title:    "UPS temperature",
		Units:    "Celsius",
		Fam:      "ups",
		Ctx:      "upsd.ups_temperature",
		Priority: prioUpsTemperature,
		Dims: module.Dims{
			{ID: "ups_%s_ups.temperature", Name: "temperature", Div: varPrecision},
		},
	}
)

var (
	upsBatteryChargePercentChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.battery_charge_percentage",
		Title:    "UPS Battery charge",
		Units:    "percentage",
		Fam:      "battery",
		Ctx:      "upsd.ups_battery_charge",
		Priority: prioBatteryCharge,
		Type:     module.Area,
		Dims: module.Dims{
			{ID: "ups_%s_battery.charge", Name: "charge", Div: varPrecision},
		},
	}
	upsBatteryEstimatedRuntimeChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.battery_estimated_runtime",
		Title:    "UPS Battery estimated runtime",
		Units:    "seconds",
		Fam:      "battery",
		Ctx:      "upsd.ups_battery_estimated_runtime",
		Priority: prioBatteryEstimatedRuntime,
		Dims: module.Dims{
			{ID: "ups_%s_battery.runtime", Name: "runtime", Div: varPrecision},
		},
	}
	upsBatteryVoltageChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.battery_voltage",
		Title:    "UPS Battery voltage",
		Units:    "Volts",
		Fam:      "battery",
		Ctx:      "upsd.ups_battery_voltage",
		Priority: prioBatteryVoltage,
		Dims: module.Dims{
			{ID: "ups_%s_battery.voltage", Name: "voltage", Div: varPrecision},
		},
	}
	upsBatteryVoltageNominalChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.battery_voltage_nominal",
		Title:    "UPS Battery voltage nominal",
		Units:    "Volts",
		Fam:      "battery",
		Ctx:      "upsd.ups_battery_voltage_nominal",
		Priority: prioBatteryVoltageNominal,
		Dims: module.Dims{
			{ID: "ups_%s_battery.voltage.nominal", Name: "nominal_voltage", Div: varPrecision},
		},
	}
)

var (
	upsInputVoltageChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.input_voltage",
		Title:    "UPS Input voltage",
		Units:    "Volts",
		Fam:      "input",
		Ctx:      "upsd.ups_input_voltage",
		Priority: prioInputVoltage,
		Dims: module.Dims{
			{ID: "ups_%s_input.voltage", Name: "voltage", Div: varPrecision},
		},
	}
	upsInputVoltageNominalChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.input_voltage_nominal",
		Title:    "UPS Input voltage nominal",
		Units:    "Volts",
		Fam:      "input",
		Ctx:      "upsd.ups_input_voltage_nominal",
		Priority: prioInputVoltageNominal,
		Dims: module.Dims{
			{ID: "ups_%s_input.voltage.nominal", Name: "nominal_voltage", Div: varPrecision},
		},
	}
	upsInputCurrentChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.input_current",
		Title:    "UPS Input current",
		Units:    "Ampere",
		Fam:      "input",
		Ctx:      "upsd.ups_input_current",
		Priority: prioInputCurrent,
		Dims: module.Dims{
			{ID: "ups_%s_input.current", Name: "current", Div: varPrecision},
		},
	}
	upsInputCurrentNominalChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.input_current_nominal",
		Title:    "UPS Input current nominal",
		Units:    "Ampere",
		Fam:      "input",
		Ctx:      "upsd.ups_input_current_nominal",
		Priority: prioInputCurrentNominal,
		Dims: module.Dims{
			{ID: "ups_%s_input.current.nominal", Name: "nominal_current", Div: varPrecision},
		},
	}
	upsInputFrequencyChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.input_frequency",
		Title:    "UPS Input frequency",
		Units:    "Hz",
		Fam:      "input",
		Ctx:      "upsd.ups_input_frequency",
		Priority: prioInputFrequency,
		Dims: module.Dims{
			{ID: "ups_%s_input.frequency", Name: "frequency", Div: varPrecision},
		},
	}
	upsInputFrequencyNominalChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.input_frequency_nominal",
		Title:    "UPS Input frequency nominal",
		Units:    "Hz",
		Fam:      "input",
		Ctx:      "upsd.ups_input_frequency_nominal",
		Priority: prioInputFrequencyNominal,
		Dims: module.Dims{
			{ID: "ups_%s_input.frequency.nominal", Name: "nominal_frequency", Div: varPrecision},
		},
	}
)

var (
	upsOutputVoltageChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.output_voltage",
		Title:    "UPS Output voltage",
		Units:    "Volts",
		Fam:      "output",
		Ctx:      "upsd.ups_output_voltage",
		Priority: prioOutputVoltage,
		Dims: module.Dims{
			{ID: "ups_%s_output.voltage", Name: "voltage", Div: varPrecision},
		},
	}
	upsOutputVoltageNominalChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.output_voltage_nominal",
		Title:    "UPS Output voltage nominal",
		Units:    "Volts",
		Fam:      "output",
		Ctx:      "upsd.ups_output_voltage_nominal",
		Priority: prioOutputVoltageNominal,
		Dims: module.Dims{
			{ID: "ups_%s_output.voltage.nominal", Name: "nominal_voltage", Div: varPrecision},
		},
	}
	upsOutputCurrentChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.output_current",
		Title:    "UPS Output current",
		Units:    "Ampere",
		Fam:      "output",
		Ctx:      "upsd.ups_output_current",
		Priority: prioOutputCurrent,
		Dims: module.Dims{
			{ID: "ups_%s_output.current", Name: "current", Div: varPrecision},
		},
	}
	upsOutputCurrentNominalChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.output_current_nominal",
		Title:    "UPS Output current nominal",
		Units:    "Ampere",
		Fam:      "output",
		Ctx:      "upsd.ups_output_current_nominal",
		Priority: prioOutputCurrentNominal,
		Dims: module.Dims{
			{ID: "ups_%s_output.current.nominal", Name: "nominal_current", Div: varPrecision},
		},
	}
	upsOutputFrequencyChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.output_frequency",
		Title:    "UPS Output frequency",
		Units:    "Hz",
		Fam:      "output",
		Ctx:      "upsd.ups_output_frequency",
		Priority: prioOutputFrequency,
		Dims: module.Dims{
			{ID: "ups_%s_output.frequency", Name: "frequency", Div: varPrecision},
		},
	}
	upsOutputFrequencyNominalChartTmpl = module.Chart{
		IDSep:    true,
		ID:       "%s.output_frequency_nominal",
		Title:    "UPS Output frequency nominal",
		Units:    "Hz",
		Fam:      "output",
		Ctx:      "upsd.ups_output_frequency_nominal",
		Priority: prioOutputFrequencyNominal,
		Dims: module.Dims{
			{ID: "ups_%s_output.frequency.nominal", Name: "nominal_frequency", Div: varPrecision},
		},
	}
)

func (u *Upsd) addUPSCharts(ups upsUnit) {
	charts := upsChartsTmpl.Copy()

	var removed []string
	for _, v := range []struct{ v, id string }{
		{varBatteryVoltage, upsBatteryVoltageChartTmpl.ID},
		{varBatteryVoltageNominal, upsBatteryVoltageNominalChartTmpl.ID},

		{varUpsTemperature, upsTemperatureChartTmpl.ID},

		{varInputVoltage, upsInputVoltageChartTmpl.ID},
		{varInputVoltageNominal, upsInputVoltageNominalChartTmpl.ID},
		{varInputCurrent, upsInputCurrentChartTmpl.ID},
		{varInputCurrentNominal, upsInputCurrentNominalChartTmpl.ID},
		{varInputFrequency, upsInputFrequencyChartTmpl.ID},
		{varInputFrequencyNominal, upsInputFrequencyNominalChartTmpl.ID},

		{varOutputVoltage, upsOutputVoltageChartTmpl.ID},
		{varOutputVoltageNominal, upsOutputVoltageNominalChartTmpl.ID},
		{varOutputCurrent, upsOutputCurrentChartTmpl.ID},
		{varOutputCurrentNominal, upsOutputCurrentNominalChartTmpl.ID},
		{varOutputFrequency, upsOutputFrequencyChartTmpl.ID},
		{varOutputFrequencyNominal, upsOutputFrequencyNominalChartTmpl.ID},
	} {
		if !hasVar(ups.vars, v.v) {
			removed = append(removed, v.v)
			_ = charts.Remove(v.id)
		}
	}

	u.Debugf("UPS '%s' no metrics: %v", ups.name, removed)

	name := cleanUpsName(ups.name)
	for _, chart := range *charts {
		chart.ID = fmt.Sprintf(chart.ID, name)
		chart.Labels = []module.Label{
			{Key: "ups_name", Value: ups.name},
			{Key: "battery_type", Value: ups.vars[varBatteryType]},
			{Key: "device_model", Value: ups.vars[varDeviceModel]},
			{Key: "device_serial", Value: ups.vars[varDeviceSerial]},
			{Key: "device_manufacturer", Value: ups.vars[varDeviceMfr]},
			{Key: "device_type", Value: ups.vars[varDeviceType]},
		}
		for _, dim := range chart.Dims {
			dim.ID = fmt.Sprintf(dim.ID, ups.name)
		}
	}

	if err := u.Charts().Add(*charts...); err != nil {
		u.Warning(err)
	}
}

func (u *Upsd) removeUPSCharts(name string) {
	name = cleanUpsName(name)
	for _, chart := range *u.Charts() {
		if strings.HasPrefix(chart.ID, name) {
			chart.MarkRemove()
			chart.MarkNotCreated()
		}
	}
}

func cleanUpsName(name string) string {
	name = strings.ReplaceAll(name, " ", "_")
	name = strings.ReplaceAll(name, ".", "_")
	return name
}
