// Copyright 2018 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 operations)
// - 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
	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
}

// 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)
}
