package nfs

import (
	"bytes"
	"context"

	"github.com/go-git/go-billy/v5"
	"github.com/willscott/go-nfs-client/nfs/xdr"
)

const (
	// FSInfoPropertyLink does the FS support hard links?
	FSInfoPropertyLink = 0x0001
	// FSInfoPropertySymlink does the FS support soft links?
	FSInfoPropertySymlink = 0x0002
	// FSInfoPropertyHomogeneous does the FS need PATHCONF calls for each file
	FSInfoPropertyHomogeneous = 0x0008
	// FSInfoPropertyCanSetTime can the FS support setting access/mod times?
	FSInfoPropertyCanSetTime = 0x0010
)

func onFSInfo(ctx context.Context, w *response, userHandle Handler) error {
	roothandle, err := xdr.ReadOpaque(w.req.Body)
	if err != nil {
		return &NFSStatusError{NFSStatusInval, err}
	}
	fs, path, err := userHandle.FromHandle(ctx, roothandle)
	if err != nil {
		return &NFSStatusError{NFSStatusStale, err}
	}

	writer := bytes.NewBuffer([]byte{})
	if err := xdr.Write(writer, uint32(NFSStatusOk)); err != nil {
		return &NFSStatusError{NFSStatusServerFault, err}
	}
	if err := WritePostOpAttrs(writer, tryStat(ctx, fs, path)); err != nil {
		return &NFSStatusError{NFSStatusServerFault, err}
	}

	type fsinfores struct {
		Rtmax       uint32
		Rtpref      uint32
		Rtmult      uint32
		Wtmax       uint32
		Wtpref      uint32
		Wtmult      uint32
		Dtpref      uint32
		Maxfilesize uint64
		TimeDelta   uint64
		Properties  uint32
	}

	res := fsinfores{
		Rtmax:       1 << 30,
		Rtpref:      1 << 30,
		Rtmult:      4096,
		Wtmax:       1 << 30,
		Wtpref:      1 << 30,
		Wtmult:      4096,
		Dtpref:      8192,
		Maxfilesize: 1 << 62, // wild guess. this seems big.
		TimeDelta:   1,       // nanosecond precision.
		Properties:  0,
	}

	// TODO: these aren't great indications of support, really.
	// if _, ok := fs.(billy.Symlink); ok {
	// 	res.Properties |= FSInfoPropertyLink
	// 	res.Properties |= FSInfoPropertySymlink
	// }
	// TODO: if the nfs share spans multiple virtual mounts, may need
	// to support granular PATHINFO responses.
	res.Properties |= FSInfoPropertyHomogeneous
	// TODO: not a perfect indicator

	if CapabilityCheck(fs, billy.WriteCapability) {
		res.Properties |= FSInfoPropertyCanSetTime
	}

	// TODO: this whole struct should be specifiable by the userhandler.

	if err := xdr.Write(writer, res); err != nil {
		return &NFSStatusError{NFSStatusServerFault, err}
	}
	if err := w.Write(writer.Bytes()); err != nil {
		return &NFSStatusError{NFSStatusServerFault, err}
	}
	return nil
}