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

// Package nfs implements parsing of /proc/net/rpc/nfsd.
// Fields are documented in https://www.svennd.be/nfsd-stats-explained-procnetrpcnfsd/
package nfs

import (
	"os"
	"strings"

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

// ReplyCache models the "rc" line.
type ReplyCache struct {
	Hits    uint64
	Misses  uint64
	NoCache uint64
}

// FileHandles models the "fh" line.
type FileHandles struct {
	Stale        uint64
	TotalLookups uint64
	AnonLookups  uint64
	DirNoCache   uint64
	NoDirNoCache uint64
}

// InputOutput models the "io" line.
type InputOutput struct {
	Read  uint64
	Write uint64
}

// Threads models the "th" line.
type Threads struct {
	Threads uint64
	FullCnt uint64
}

// ReadAheadCache models the "ra" line.
type ReadAheadCache struct {
	CacheSize      uint64
	CacheHistogram []uint64
	NotFound       uint64
}

// Network models the "net" line.
type Network struct {
	NetCount   uint64
	UDPCount   uint64
	TCPCount   uint64
	TCPConnect uint64
}

// ClientRPC models the nfs "rpc" line.
type ClientRPC struct {
	RPCCount        uint64
	Retransmissions uint64
	AuthRefreshes   uint64
}

// ServerRPC models the nfsd "rpc" line.
type ServerRPC struct {
	RPCCount uint64
	BadCnt   uint64
	BadFmt   uint64
	BadAuth  uint64
	BadcInt  uint64
}

// V2Stats models the "proc2" line.
type V2Stats struct {
	Null     uint64
	GetAttr  uint64
	SetAttr  uint64
	Root     uint64
	Lookup   uint64
	ReadLink uint64
	Read     uint64
	WrCache  uint64
	Write    uint64
	Create   uint64
	Remove   uint64
	Rename   uint64
	Link     uint64
	SymLink  uint64
	MkDir    uint64
	RmDir    uint64
	ReadDir  uint64
	FsStat   uint64
}

// V3Stats models the "proc3" line.
type V3Stats struct {
	Null        uint64
	GetAttr     uint64
	SetAttr     uint64
	Lookup      uint64
	Access      uint64
	ReadLink    uint64
	Read        uint64
	Write       uint64
	Create      uint64
	MkDir       uint64
	SymLink     uint64
	MkNod       uint64
	Remove      uint64
	RmDir       uint64
	Rename      uint64
	Link        uint64
	ReadDir     uint64
	ReadDirPlus uint64
	FsStat      uint64
	FsInfo      uint64
	PathConf    uint64
	Commit      uint64
}

// ClientV4Stats models the nfs "proc4" line.
type ClientV4Stats struct {
	Null               uint64
	Read               uint64
	Write              uint64
	Commit             uint64
	Open               uint64
	OpenConfirm        uint64
	OpenNoattr         uint64
	OpenDowngrade      uint64
	Close              uint64
	Setattr            uint64
	FsInfo             uint64
	Renew              uint64
	SetClientID        uint64
	SetClientIDConfirm uint64
	Lock               uint64
	Lockt              uint64
	Locku              uint64
	Access             uint64
	Getattr            uint64
	Lookup             uint64
	LookupRoot         uint64
	Remove             uint64
	Rename             uint64
	Link               uint64
	Symlink            uint64
	Create             uint64
	Pathconf           uint64
	StatFs             uint64
	ReadLink           uint64
	ReadDir            uint64
	ServerCaps         uint64
	DelegReturn        uint64
	GetACL             uint64
	SetACL             uint64
	FsLocations        uint64
	ReleaseLockowner   uint64
	Secinfo            uint64
	FsidPresent        uint64
	ExchangeID         uint64
	CreateSession      uint64
	DestroySession     uint64
	Sequence           uint64
	GetLeaseTime       uint64
	ReclaimComplete    uint64
	LayoutGet          uint64
	GetDeviceInfo      uint64
	LayoutCommit       uint64
	LayoutReturn       uint64
	SecinfoNoName      uint64
	TestStateID        uint64
	FreeStateID        uint64
	GetDeviceList      uint64
	BindConnToSession  uint64
	DestroyClientID    uint64
	Seek               uint64
	Allocate           uint64
	DeAllocate         uint64
	LayoutStats        uint64
	Clone              uint64
}

// ServerV4Stats models the nfsd "proc4" line.
type ServerV4Stats struct {
	Null     uint64
	Compound uint64
}

// V4Ops models the "proc4ops" line: NFSv4 operations
// Variable list.
// See:
// - v4.0 https://tools.ietf.org/html/rfc3010 (38/39 operations)
//   - nfs == v2.5.x 38 field : https://elixir.bootlin.com/linux/v2.5.75/source/include/linux/nfs4.h#L52
//   - nfs >= v2.6.x 39 field : https://elixir.bootlin.com/linux/v2.6.39.4/source/include/linux/nfs4.h#L233
//
// - v4.1 https://tools.ietf.org/html/rfc5661 (58 operations)
// - v4.2 https://tools.ietf.org/html/draft-ietf-nfsv4-minorversion2-41 (71 operations)
//
//nolint:godot
type V4Ops struct {
	//Values       uint64 // Variable depending on v4.x sub-version. TODO: Will this always at least include the fields in this struct?
	Op0Unused          uint64
	Op1Unused          uint64
	Op2Future          uint64
	Access             uint64
	Close              uint64
	Commit             uint64
	Create             uint64
	DelegPurge         uint64
	DelegReturn        uint64
	GetAttr            uint64
	GetFH              uint64
	Link               uint64
	Lock               uint64
	Lockt              uint64
	Locku              uint64
	Lookup             uint64
	LookupRoot         uint64
	Nverify            uint64
	Open               uint64
	OpenAttr           uint64
	OpenConfirm        uint64
	OpenDgrd           uint64
	PutFH              uint64
	PutPubFH           uint64
	PutRootFH          uint64
	Read               uint64
	ReadDir            uint64
	ReadLink           uint64
	Remove             uint64
	Rename             uint64
	Renew              uint64
	RestoreFH          uint64
	SaveFH             uint64
	SecInfo            uint64
	SetAttr            uint64
	SetClientID        uint64
	SetClientIDConfirm uint64
	Verify             uint64
	Write              uint64
	RelLockOwner       uint64
}

// ClientRPCStats models all stats from /proc/net/rpc/nfs.
type ClientRPCStats struct {
	Network       Network
	ClientRPC     ClientRPC
	V2Stats       V2Stats
	V3Stats       V3Stats
	ClientV4Stats ClientV4Stats
}

// ServerRPCStats models all stats from /proc/net/rpc/nfsd.
type ServerRPCStats struct {
	ReplyCache     ReplyCache
	FileHandles    FileHandles
	InputOutput    InputOutput
	Threads        Threads
	ReadAheadCache ReadAheadCache
	Network        Network
	ServerRPC      ServerRPC
	V2Stats        V2Stats
	V3Stats        V3Stats
	ServerV4Stats  ServerV4Stats
	V4Ops          V4Ops
	WdelegGetattr  uint64
}

// FS represents the pseudo-filesystem proc, which provides an interface to
// kernel data structures.
type FS struct {
	proc *fs.FS
}

// NewDefaultFS returns a new FS mounted under the default mountPoint. It will error
// if the mount point can't be read.
func NewDefaultFS() (FS, error) {
	return NewFS(fs.DefaultProcMountPoint)
}

// NewFS returns a new FS mounted under the given mountPoint. It will error
// if the mount point can't be read.
func NewFS(mountPoint string) (FS, error) {
	if strings.TrimSpace(mountPoint) == "" {
		mountPoint = fs.DefaultProcMountPoint
	}
	fs, err := fs.NewFS(mountPoint)
	if err != nil {
		return FS{}, err
	}
	return FS{&fs}, nil
}

// ClientRPCStats retrieves NFS client RPC statistics
// from proc/net/rpc/nfs.
func (fs FS) ClientRPCStats() (*ClientRPCStats, error) {
	f, err := os.Open(fs.proc.Path("net/rpc/nfs"))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	return ParseClientRPCStats(f)
}

// ServerRPCStats retrieves NFS daemon RPC statistics
// from proc/net/rpc/nfsd.
func (fs FS) ServerRPCStats() (*ServerRPCStats, error) {
	f, err := os.Open(fs.proc.Path("net/rpc/nfsd"))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	return ParseServerRPCStats(f)
}
