package apcupsdexporter

import (
	"regexp"
	"strings"
	"testing"
	"time"

	"github.com/mdlayher/apcupsd"
)

func TestUPSCollector(t *testing.T) {
	var tests = []struct {
		desc    string
		ss      *testStatusSource
		matches []*regexp.Regexp
	}{
		{
			desc: "empty",
			ss: &testStatusSource{
				s: &apcupsd.Status{},
			},
			matches: []*regexp.Regexp{
				regexp.MustCompile(`apcupsd_battery_charge_percent 0`),
			},
		},
		{
			desc: "full",
			ss: &testStatusSource{
				s: &apcupsd.Status{
					Hostname: "a",
					Model:    "b",
					UPSName:  "c",

					BatteryChargePercent:    100.0,
					CumulativeTimeOnBattery: 30 * time.Second,
					NominalBatteryVoltage:   12.0,
					TimeLeft:                2 * time.Minute,
					TimeOnBattery:           10 * time.Second,
					BatteryVoltage:          13.2,
					NominalInputVoltage:     120.0,
					LineVoltage:             121.1,
					LoadPercent:             16.0,
					NumberTransfers:         1,
					XOnBattery:              time.Unix(100001, 0),
					XOffBattery:             time.Unix(100002, 0),
					LastSelftest:            time.Unix(100003, 0),
					NominalPower:            50.0,
					Status:                  "ONLINE",
				},
			},
			matches: []*regexp.Regexp{
				regexp.MustCompile(`apcupsd_battery_charge_percent 100`),
				regexp.MustCompile(`apcupsd_battery_cumulative_time_on_seconds_total 30`),
				regexp.MustCompile(`apcupsd_battery_nominal_volts 12`),
				regexp.MustCompile(`apcupsd_battery_time_left_seconds 120`),
				regexp.MustCompile(`apcupsd_battery_time_on_seconds 10`),
				regexp.MustCompile(`apcupsd_battery_volts 13.2`),
				regexp.MustCompile(`apcupsd_battery_number_transfers_total 1`),
				regexp.MustCompile(`apcupsd_line_nominal_volts 120`),
				regexp.MustCompile(`apcupsd_line_volts 121.1`),
				regexp.MustCompile(`apcupsd_ups_load_percent 16`),
				regexp.MustCompile(`apcupsd_last_transfer_on_battery 100001`),
				regexp.MustCompile(`apcupsd_last_transfer_off_battery 100002`),
				regexp.MustCompile(`apcupsd_last_selftest 100003`),
				regexp.MustCompile(`apcupsd_nominal_power_watts 50`),
				regexp.MustCompile(`apcupsd_ups_info{hostname="a",model="b",ups_name="c"} 1`),
				regexp.MustCompile(`apcupsd_ups_status{status="ONLINE"} 1`),
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			out := testCollector(t, NewUPSCollector(tt.ss))

			for _, m := range tt.matches {
				name := metricName(t, m.String())

				t.Run(name, func(t *testing.T) {
					if !m.Match(out) {
						t.Fatalf("\toutput failed to match regex (regexp: %v)", m)
					}
				})
			}
		})
	}
}

// TestZeroTimesAreIgnored tests that times with a zero value (time.IsZero() == true)
// are not collected.
func TestZeroTimesAreIgnored(t *testing.T) {
	ss := &testStatusSource{
		s: &apcupsd.Status{
			XOnBattery:  time.Unix(123456, 0),
			XOffBattery: time.Time{},
		},
	}
	out := testCollector(t, NewUPSCollector(ss))
	// Test that in general timestamps are collected.
	if !regexp.MustCompile(`apcupsd_last_transfer_on_battery.* 123456`).Match(out) {
		t.Error("non-zero timestamp is not reported properly")
	}
	// Test that zero timestamps, however, are ignored.
	if regexp.MustCompile(`apcupsd_last_transfer_off_battery`).Match(out) {
		t.Error("Zero time is reported")
	}
}

func metricName(t *testing.T, metric string) string {
	ss := strings.Split(metric, " ")
	if len(ss) != 2 {
		t.Fatalf("malformed metric: %v", metric)
	}

	if !strings.Contains(ss[0], "{") {
		return ss[0]
	}

	ss = strings.Split(ss[0], "{")
	if len(ss) != 2 {
		t.Fatalf("malformed metric: %v", metric)
	}

	return ss[0]
}

var _ StatusSource = &testStatusSource{}

type testStatusSource struct {
	s *apcupsd.Status
}

func (ss *testStatusSource) Status() (*apcupsd.Status, error) {
	return ss.s, nil
}
