// Copyright 2022 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.

package procfs

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"os"
	"strconv"
	"strings"

	"github.com/prometheus/procfs/internal/util"
)

// ProcSnmp6 models the content of /proc/<pid>/net/snmp6.
type ProcSnmp6 struct {
	// The process ID.
	PID int
	Ip6
	Icmp6
	Udp6
	UdpLite6
}

type Ip6 struct { // nolint:revive
	InReceives       *float64
	InHdrErrors      *float64
	InTooBigErrors   *float64
	InNoRoutes       *float64
	InAddrErrors     *float64
	InUnknownProtos  *float64
	InTruncatedPkts  *float64
	InDiscards       *float64
	InDelivers       *float64
	OutForwDatagrams *float64
	OutRequests      *float64
	OutDiscards      *float64
	OutNoRoutes      *float64
	ReasmTimeout     *float64
	ReasmReqds       *float64
	ReasmOKs         *float64
	ReasmFails       *float64
	FragOKs          *float64
	FragFails        *float64
	FragCreates      *float64
	InMcastPkts      *float64
	OutMcastPkts     *float64
	InOctets         *float64
	OutOctets        *float64
	InMcastOctets    *float64
	OutMcastOctets   *float64
	InBcastOctets    *float64
	OutBcastOctets   *float64
	InNoECTPkts      *float64
	InECT1Pkts       *float64
	InECT0Pkts       *float64
	InCEPkts         *float64
}

type Icmp6 struct {
	InMsgs                    *float64
	InErrors                  *float64
	OutMsgs                   *float64
	OutErrors                 *float64
	InCsumErrors              *float64
	InDestUnreachs            *float64
	InPktTooBigs              *float64
	InTimeExcds               *float64
	InParmProblems            *float64
	InEchos                   *float64
	InEchoReplies             *float64
	InGroupMembQueries        *float64
	InGroupMembResponses      *float64
	InGroupMembReductions     *float64
	InRouterSolicits          *float64
	InRouterAdvertisements    *float64
	InNeighborSolicits        *float64
	InNeighborAdvertisements  *float64
	InRedirects               *float64
	InMLDv2Reports            *float64
	OutDestUnreachs           *float64
	OutPktTooBigs             *float64
	OutTimeExcds              *float64
	OutParmProblems           *float64
	OutEchos                  *float64
	OutEchoReplies            *float64
	OutGroupMembQueries       *float64
	OutGroupMembResponses     *float64
	OutGroupMembReductions    *float64
	OutRouterSolicits         *float64
	OutRouterAdvertisements   *float64
	OutNeighborSolicits       *float64
	OutNeighborAdvertisements *float64
	OutRedirects              *float64
	OutMLDv2Reports           *float64
	InType1                   *float64
	InType134                 *float64
	InType135                 *float64
	InType136                 *float64
	InType143                 *float64
	OutType133                *float64
	OutType135                *float64
	OutType136                *float64
	OutType143                *float64
}

type Udp6 struct { // nolint:revive
	InDatagrams  *float64
	NoPorts      *float64
	InErrors     *float64
	OutDatagrams *float64
	RcvbufErrors *float64
	SndbufErrors *float64
	InCsumErrors *float64
	IgnoredMulti *float64
}

type UdpLite6 struct { // nolint:revive
	InDatagrams  *float64
	NoPorts      *float64
	InErrors     *float64
	OutDatagrams *float64
	RcvbufErrors *float64
	SndbufErrors *float64
	InCsumErrors *float64
}

func (p Proc) Snmp6() (ProcSnmp6, error) {
	filename := p.path("net/snmp6")
	data, err := util.ReadFileNoStat(filename)
	if err != nil {
		// On systems with IPv6 disabled, this file won't exist.
		// Do nothing.
		if errors.Is(err, os.ErrNotExist) {
			return ProcSnmp6{PID: p.PID}, nil
		}

		return ProcSnmp6{PID: p.PID}, err
	}

	procSnmp6, err := parseSNMP6Stats(bytes.NewReader(data))
	procSnmp6.PID = p.PID
	return procSnmp6, err
}

// parseSnmp6 parses the metrics from proc/<pid>/net/snmp6 file
// and returns a map contains those metrics.
func parseSNMP6Stats(r io.Reader) (ProcSnmp6, error) {
	var (
		scanner   = bufio.NewScanner(r)
		procSnmp6 = ProcSnmp6{}
	)

	for scanner.Scan() {
		stat := strings.Fields(scanner.Text())
		if len(stat) < 2 {
			continue
		}
		// Expect to have "6" in metric name, skip line otherwise
		if sixIndex := strings.Index(stat[0], "6"); sixIndex != -1 {
			protocol := stat[0][:sixIndex+1]
			key := stat[0][sixIndex+1:]
			value, err := strconv.ParseFloat(stat[1], 64)
			if err != nil {
				return procSnmp6, err
			}

			switch protocol {
			case "Ip6":
				switch key {
				case "InReceives":
					procSnmp6.Ip6.InReceives = &value
				case "InHdrErrors":
					procSnmp6.Ip6.InHdrErrors = &value
				case "InTooBigErrors":
					procSnmp6.Ip6.InTooBigErrors = &value
				case "InNoRoutes":
					procSnmp6.Ip6.InNoRoutes = &value
				case "InAddrErrors":
					procSnmp6.Ip6.InAddrErrors = &value
				case "InUnknownProtos":
					procSnmp6.Ip6.InUnknownProtos = &value
				case "InTruncatedPkts":
					procSnmp6.Ip6.InTruncatedPkts = &value
				case "InDiscards":
					procSnmp6.Ip6.InDiscards = &value
				case "InDelivers":
					procSnmp6.Ip6.InDelivers = &value
				case "OutForwDatagrams":
					procSnmp6.Ip6.OutForwDatagrams = &value
				case "OutRequests":
					procSnmp6.Ip6.OutRequests = &value
				case "OutDiscards":
					procSnmp6.Ip6.OutDiscards = &value
				case "OutNoRoutes":
					procSnmp6.Ip6.OutNoRoutes = &value
				case "ReasmTimeout":
					procSnmp6.Ip6.ReasmTimeout = &value
				case "ReasmReqds":
					procSnmp6.Ip6.ReasmReqds = &value
				case "ReasmOKs":
					procSnmp6.Ip6.ReasmOKs = &value
				case "ReasmFails":
					procSnmp6.Ip6.ReasmFails = &value
				case "FragOKs":
					procSnmp6.Ip6.FragOKs = &value
				case "FragFails":
					procSnmp6.Ip6.FragFails = &value
				case "FragCreates":
					procSnmp6.Ip6.FragCreates = &value
				case "InMcastPkts":
					procSnmp6.Ip6.InMcastPkts = &value
				case "OutMcastPkts":
					procSnmp6.Ip6.OutMcastPkts = &value
				case "InOctets":
					procSnmp6.Ip6.InOctets = &value
				case "OutOctets":
					procSnmp6.Ip6.OutOctets = &value
				case "InMcastOctets":
					procSnmp6.Ip6.InMcastOctets = &value
				case "OutMcastOctets":
					procSnmp6.Ip6.OutMcastOctets = &value
				case "InBcastOctets":
					procSnmp6.Ip6.InBcastOctets = &value
				case "OutBcastOctets":
					procSnmp6.Ip6.OutBcastOctets = &value
				case "InNoECTPkts":
					procSnmp6.Ip6.InNoECTPkts = &value
				case "InECT1Pkts":
					procSnmp6.Ip6.InECT1Pkts = &value
				case "InECT0Pkts":
					procSnmp6.Ip6.InECT0Pkts = &value
				case "InCEPkts":
					procSnmp6.Ip6.InCEPkts = &value

				}
			case "Icmp6":
				switch key {
				case "InMsgs":
					procSnmp6.Icmp6.InMsgs = &value
				case "InErrors":
					procSnmp6.Icmp6.InErrors = &value
				case "OutMsgs":
					procSnmp6.Icmp6.OutMsgs = &value
				case "OutErrors":
					procSnmp6.Icmp6.OutErrors = &value
				case "InCsumErrors":
					procSnmp6.Icmp6.InCsumErrors = &value
				case "InDestUnreachs":
					procSnmp6.Icmp6.InDestUnreachs = &value
				case "InPktTooBigs":
					procSnmp6.Icmp6.InPktTooBigs = &value
				case "InTimeExcds":
					procSnmp6.Icmp6.InTimeExcds = &value
				case "InParmProblems":
					procSnmp6.Icmp6.InParmProblems = &value
				case "InEchos":
					procSnmp6.Icmp6.InEchos = &value
				case "InEchoReplies":
					procSnmp6.Icmp6.InEchoReplies = &value
				case "InGroupMembQueries":
					procSnmp6.Icmp6.InGroupMembQueries = &value
				case "InGroupMembResponses":
					procSnmp6.Icmp6.InGroupMembResponses = &value
				case "InGroupMembReductions":
					procSnmp6.Icmp6.InGroupMembReductions = &value
				case "InRouterSolicits":
					procSnmp6.Icmp6.InRouterSolicits = &value
				case "InRouterAdvertisements":
					procSnmp6.Icmp6.InRouterAdvertisements = &value
				case "InNeighborSolicits":
					procSnmp6.Icmp6.InNeighborSolicits = &value
				case "InNeighborAdvertisements":
					procSnmp6.Icmp6.InNeighborAdvertisements = &value
				case "InRedirects":
					procSnmp6.Icmp6.InRedirects = &value
				case "InMLDv2Reports":
					procSnmp6.Icmp6.InMLDv2Reports = &value
				case "OutDestUnreachs":
					procSnmp6.Icmp6.OutDestUnreachs = &value
				case "OutPktTooBigs":
					procSnmp6.Icmp6.OutPktTooBigs = &value
				case "OutTimeExcds":
					procSnmp6.Icmp6.OutTimeExcds = &value
				case "OutParmProblems":
					procSnmp6.Icmp6.OutParmProblems = &value
				case "OutEchos":
					procSnmp6.Icmp6.OutEchos = &value
				case "OutEchoReplies":
					procSnmp6.Icmp6.OutEchoReplies = &value
				case "OutGroupMembQueries":
					procSnmp6.Icmp6.OutGroupMembQueries = &value
				case "OutGroupMembResponses":
					procSnmp6.Icmp6.OutGroupMembResponses = &value
				case "OutGroupMembReductions":
					procSnmp6.Icmp6.OutGroupMembReductions = &value
				case "OutRouterSolicits":
					procSnmp6.Icmp6.OutRouterSolicits = &value
				case "OutRouterAdvertisements":
					procSnmp6.Icmp6.OutRouterAdvertisements = &value
				case "OutNeighborSolicits":
					procSnmp6.Icmp6.OutNeighborSolicits = &value
				case "OutNeighborAdvertisements":
					procSnmp6.Icmp6.OutNeighborAdvertisements = &value
				case "OutRedirects":
					procSnmp6.Icmp6.OutRedirects = &value
				case "OutMLDv2Reports":
					procSnmp6.Icmp6.OutMLDv2Reports = &value
				case "InType1":
					procSnmp6.Icmp6.InType1 = &value
				case "InType134":
					procSnmp6.Icmp6.InType134 = &value
				case "InType135":
					procSnmp6.Icmp6.InType135 = &value
				case "InType136":
					procSnmp6.Icmp6.InType136 = &value
				case "InType143":
					procSnmp6.Icmp6.InType143 = &value
				case "OutType133":
					procSnmp6.Icmp6.OutType133 = &value
				case "OutType135":
					procSnmp6.Icmp6.OutType135 = &value
				case "OutType136":
					procSnmp6.Icmp6.OutType136 = &value
				case "OutType143":
					procSnmp6.Icmp6.OutType143 = &value
				}
			case "Udp6":
				switch key {
				case "InDatagrams":
					procSnmp6.Udp6.InDatagrams = &value
				case "NoPorts":
					procSnmp6.Udp6.NoPorts = &value
				case "InErrors":
					procSnmp6.Udp6.InErrors = &value
				case "OutDatagrams":
					procSnmp6.Udp6.OutDatagrams = &value
				case "RcvbufErrors":
					procSnmp6.Udp6.RcvbufErrors = &value
				case "SndbufErrors":
					procSnmp6.Udp6.SndbufErrors = &value
				case "InCsumErrors":
					procSnmp6.Udp6.InCsumErrors = &value
				case "IgnoredMulti":
					procSnmp6.Udp6.IgnoredMulti = &value
				}
			case "UdpLite6":
				switch key {
				case "InDatagrams":
					procSnmp6.UdpLite6.InDatagrams = &value
				case "NoPorts":
					procSnmp6.UdpLite6.NoPorts = &value
				case "InErrors":
					procSnmp6.UdpLite6.InErrors = &value
				case "OutDatagrams":
					procSnmp6.UdpLite6.OutDatagrams = &value
				case "RcvbufErrors":
					procSnmp6.UdpLite6.RcvbufErrors = &value
				case "SndbufErrors":
					procSnmp6.UdpLite6.SndbufErrors = &value
				case "InCsumErrors":
					procSnmp6.UdpLite6.InCsumErrors = &value
				}
			}
		}
	}
	return procSnmp6, scanner.Err()
}