// SPDX-License-Identifier: Apache-2.0
//
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build windows

package hyperv

import (
	"fmt"

	"github.com/prometheus-community/windows_exporter/internal/pdh"
	"github.com/prometheus-community/windows_exporter/internal/types"
	"github.com/prometheus/client_golang/prometheus"
)

// collectorVirtualMachineHealthSummary Hyper-V Virtual Switch Summary metrics
type collectorVirtualSwitch struct {
	perfDataCollectorVirtualSwitch *pdh.Collector
	perfDataObjectVirtualSwitch    []perfDataCounterValuesVirtualSwitch

	virtualSwitchBroadcastPacketsReceived         *prometheus.Desc // \Hyper-V Virtual Switch(*)\Broadcast Packets Received/sec
	virtualSwitchBroadcastPacketsSent             *prometheus.Desc // \Hyper-V Virtual Switch(*)\Broadcast Packets Sent/sec
	virtualSwitchBytes                            *prometheus.Desc // \Hyper-V Virtual Switch(*)\Bytes/sec
	virtualSwitchBytesReceived                    *prometheus.Desc // \Hyper-V Virtual Switch(*)\Bytes Received/sec
	virtualSwitchBytesSent                        *prometheus.Desc // \Hyper-V Virtual Switch(*)\Bytes Sent/sec
	virtualSwitchDirectedPacketsReceived          *prometheus.Desc // \Hyper-V Virtual Switch(*)\Directed Packets Received/sec
	virtualSwitchDirectedPacketsSent              *prometheus.Desc // \Hyper-V Virtual Switch(*)\Directed Packets Sent/sec
	virtualSwitchDroppedPacketsIncoming           *prometheus.Desc // \Hyper-V Virtual Switch(*)\Dropped Packets Incoming/sec
	virtualSwitchDroppedPacketsOutgoing           *prometheus.Desc // \Hyper-V Virtual Switch(*)\Dropped Packets Outgoing/sec
	virtualSwitchExtensionsDroppedPacketsIncoming *prometheus.Desc // \Hyper-V Virtual Switch(*)\Extensions Dropped Packets Incoming/sec
	virtualSwitchExtensionsDroppedPacketsOutgoing *prometheus.Desc // \Hyper-V Virtual Switch(*)\Extensions Dropped Packets Outgoing/sec
	virtualSwitchLearnedMacAddresses              *prometheus.Desc // \Hyper-V Virtual Switch(*)\Learned Mac Addresses
	virtualSwitchMulticastPacketsReceived         *prometheus.Desc // \Hyper-V Virtual Switch(*)\Multicast Packets Received/sec
	virtualSwitchMulticastPacketsSent             *prometheus.Desc // \Hyper-V Virtual Switch(*)\Multicast Packets Sent/sec
	virtualSwitchNumberOfSendChannelMoves         *prometheus.Desc // \Hyper-V Virtual Switch(*)\Number of Send Channel Moves/sec
	virtualSwitchNumberOfVMQMoves                 *prometheus.Desc // \Hyper-V Virtual Switch(*)\Number of VMQ Moves/sec
	virtualSwitchPacketsFlooded                   *prometheus.Desc // \Hyper-V Virtual Switch(*)\Packets Flooded
	virtualSwitchPackets                          *prometheus.Desc // \Hyper-V Virtual Switch(*)\Packets/sec
	virtualSwitchPacketsReceived                  *prometheus.Desc // \Hyper-V Virtual Switch(*)\Packets Received/sec
	virtualSwitchPacketsSent                      *prometheus.Desc // \Hyper-V Virtual Switch(*)\Packets Sent/sec
	virtualSwitchPurgedMacAddresses               *prometheus.Desc // \Hyper-V Virtual Switch(*)\Purged Mac Addresses
}

type perfDataCounterValuesVirtualSwitch struct {
	Name string

	VirtualSwitchBroadcastPacketsReceived         float64 `perfdata:"Broadcast Packets Received/sec"`
	VirtualSwitchBroadcastPacketsSent             float64 `perfdata:"Broadcast Packets Sent/sec"`
	VirtualSwitchBytes                            float64 `perfdata:"Bytes/sec"`
	VirtualSwitchBytesReceived                    float64 `perfdata:"Bytes Received/sec"`
	VirtualSwitchBytesSent                        float64 `perfdata:"Bytes Sent/sec"`
	VirtualSwitchDirectedPacketsReceived          float64 `perfdata:"Directed Packets Received/sec"`
	VirtualSwitchDirectedPacketsSent              float64 `perfdata:"Directed Packets Sent/sec"`
	VirtualSwitchDroppedPacketsIncoming           float64 `perfdata:"Dropped Packets Incoming/sec"`
	VirtualSwitchDroppedPacketsOutgoing           float64 `perfdata:"Dropped Packets Outgoing/sec"`
	VirtualSwitchExtensionsDroppedPacketsIncoming float64 `perfdata:"Extensions Dropped Packets Incoming/sec"`
	VirtualSwitchExtensionsDroppedPacketsOutgoing float64 `perfdata:"Extensions Dropped Packets Outgoing/sec"`
	VirtualSwitchLearnedMacAddresses              float64 `perfdata:"Learned Mac Addresses"`
	VirtualSwitchMulticastPacketsReceived         float64 `perfdata:"Multicast Packets Received/sec"`
	VirtualSwitchMulticastPacketsSent             float64 `perfdata:"Multicast Packets Sent/sec"`
	VirtualSwitchNumberOfSendChannelMoves         float64 `perfdata:"Number of Send Channel Moves/sec"`
	VirtualSwitchNumberOfVMQMoves                 float64 `perfdata:"Number of VMQ Moves/sec"`
	VirtualSwitchPacketsFlooded                   float64 `perfdata:"Packets Flooded"`
	VirtualSwitchPackets                          float64 `perfdata:"Packets/sec"`
	VirtualSwitchPacketsReceived                  float64 `perfdata:"Packets Received/sec"`
	VirtualSwitchPacketsSent                      float64 `perfdata:"Packets Sent/sec"`
	VirtualSwitchPurgedMacAddresses               float64 `perfdata:"Purged Mac Addresses"`
}

func (c *Collector) buildVirtualSwitch() error {
	var err error

	c.perfDataCollectorVirtualSwitch, err = pdh.NewCollector[perfDataCounterValuesVirtualSwitch](c.logger, pdh.CounterTypeRaw, "Hyper-V Virtual Switch", pdh.InstancesAll)
	if err != nil {
		return fmt.Errorf("failed to create Hyper-V Virtual Switch collector: %w", err)
	}

	c.virtualSwitchBroadcastPacketsReceived = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_broadcast_packets_received_total"),
		"Represents the total number of broadcast packets received per second by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchBroadcastPacketsSent = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_broadcast_packets_sent_total"),
		"Represents the total number of broadcast packets sent per second by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchBytes = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_bytes_total"),
		"Represents the total number of bytes per second traversing the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchBytesReceived = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_bytes_received_total"),
		"Represents the total number of bytes received per second by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchBytesSent = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_bytes_sent_total"),
		"Represents the total number of bytes sent per second by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchDirectedPacketsReceived = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_directed_packets_received_total"),
		"Represents the total number of directed packets received per second by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchDirectedPacketsSent = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_directed_packets_send_total"),
		"Represents the total number of directed packets sent per second by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchDroppedPacketsIncoming = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_dropped_packets_incoming_total"),
		"Represents the total number of packet dropped per second by the virtual switch in the incoming direction",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchDroppedPacketsOutgoing = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_dropped_packets_outcoming_total"),
		"Represents the total number of packet dropped per second by the virtual switch in the outgoing direction",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchExtensionsDroppedPacketsIncoming = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_extensions_dropped_packets_incoming_total"),
		"Represents the total number of packet dropped per second by the virtual switch extensions in the incoming direction",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchExtensionsDroppedPacketsOutgoing = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_extensions_dropped_packets_outcoming_total"),
		"Represents the total number of packet dropped per second by the virtual switch extensions in the outgoing direction",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchLearnedMacAddresses = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_learned_mac_addresses_total"),
		"Represents the total number of learned MAC addresses of the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchMulticastPacketsReceived = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_multicast_packets_received_total"),
		"Represents the total number of multicast packets received per second by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchMulticastPacketsSent = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_multicast_packets_sent_total"),
		"Represents the total number of multicast packets sent per second by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchNumberOfSendChannelMoves = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_number_of_send_channel_moves_total"),
		"Represents the total number of send channel moves per second on this virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchNumberOfVMQMoves = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_number_of_vmq_moves_total"),
		"Represents the total number of VMQ moves per second on this virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchPacketsFlooded = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_packets_flooded_total"),
		"Represents the total number of packets flooded by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchPackets = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_packets_total"),
		"Represents the total number of packets per second traversing the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchPacketsReceived = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_packets_received_total"),
		"Represents the total number of packets received per second by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchPacketsSent = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_packets_sent_total"),
		"Represents the total number of packets send per second by the virtual switch",
		[]string{"vswitch"},
		nil,
	)
	c.virtualSwitchPurgedMacAddresses = prometheus.NewDesc(
		prometheus.BuildFQName(types.Namespace, Name, "vswitch_purged_mac_addresses_total"),
		"Represents the total number of purged MAC addresses of the virtual switch",
		[]string{"vswitch"},
		nil,
	)

	return nil
}

func (c *Collector) collectVirtualSwitch(ch chan<- prometheus.Metric) error {
	err := c.perfDataCollectorVirtualSwitch.Collect(&c.perfDataObjectVirtualSwitch)
	if err != nil {
		return fmt.Errorf("failed to collect Hyper-V Virtual Switch metrics: %w", err)
	}

	for _, data := range c.perfDataObjectVirtualSwitch {
		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchBroadcastPacketsReceived,
			prometheus.CounterValue,
			data.VirtualSwitchBroadcastPacketsReceived,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchBroadcastPacketsSent,
			prometheus.CounterValue,
			data.VirtualSwitchBroadcastPacketsSent,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchBytes,
			prometheus.CounterValue,
			data.VirtualSwitchBytes,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchBytesReceived,
			prometheus.CounterValue,
			data.VirtualSwitchBytesReceived,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchBytesSent,
			prometheus.CounterValue,
			data.VirtualSwitchBytesSent,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchDirectedPacketsReceived,
			prometheus.CounterValue,
			data.VirtualSwitchDirectedPacketsReceived,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchDirectedPacketsSent,
			prometheus.CounterValue,
			data.VirtualSwitchDirectedPacketsSent,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchDroppedPacketsIncoming,
			prometheus.CounterValue,
			data.VirtualSwitchDroppedPacketsIncoming,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchDroppedPacketsOutgoing,
			prometheus.CounterValue,
			data.VirtualSwitchDroppedPacketsOutgoing,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchExtensionsDroppedPacketsIncoming,
			prometheus.CounterValue,
			data.VirtualSwitchExtensionsDroppedPacketsIncoming,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchExtensionsDroppedPacketsOutgoing,
			prometheus.CounterValue,
			data.VirtualSwitchExtensionsDroppedPacketsOutgoing,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchLearnedMacAddresses,
			prometheus.CounterValue,
			data.VirtualSwitchLearnedMacAddresses,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchMulticastPacketsReceived,
			prometheus.CounterValue,
			data.VirtualSwitchMulticastPacketsReceived,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchMulticastPacketsSent,
			prometheus.CounterValue,
			data.VirtualSwitchMulticastPacketsSent,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchNumberOfSendChannelMoves,
			prometheus.CounterValue,
			data.VirtualSwitchNumberOfSendChannelMoves,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchNumberOfVMQMoves,
			prometheus.CounterValue,
			data.VirtualSwitchNumberOfVMQMoves,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchPacketsFlooded,
			prometheus.CounterValue,
			data.VirtualSwitchPacketsFlooded,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchPackets,
			prometheus.CounterValue,
			data.VirtualSwitchPackets,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchPacketsReceived,
			prometheus.CounterValue,
			data.VirtualSwitchPacketsReceived,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchPacketsSent,
			prometheus.CounterValue,
			data.VirtualSwitchPacketsSent,
			data.Name,
		)

		ch <- prometheus.MustNewConstMetric(
			c.virtualSwitchPurgedMacAddresses,
			prometheus.CounterValue,
			data.VirtualSwitchPurgedMacAddresses,
			data.Name,
		)
	}

	return nil
}
