package collectors

import (
	"encoding/json"
	"fmt"
	"log"

	//	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	lp "github.com/ClusterCockpit/cc-metric-collector/internal/ccMetric"
)

type NfsCollectorData struct {
	current int64
	last    int64
}

type NfsCollector struct {
	metricCollector
	tags   map[string]string
	config struct {
		Nfsutils       string   `json:"nfsutils"`
		ExcludeMetrics []string `json:"exclude_metrics,omitempty"`
	}
	data map[string]map[string]NfsCollectorData
}

func (m *NfsCollector) initStats() error {
	cmd := exec.Command(m.config.Nfsutils, "-l")
	cmd.Wait()
	buffer, err := cmd.Output()
	if err == nil {
		for _, line := range strings.Split(string(buffer), "\n") {
			lf := strings.Fields(line)
			if len(lf) != 5 {
				continue
			}
			if _, exist := m.data[lf[1]]; !exist {
				m.data[lf[1]] = make(map[string]NfsCollectorData)
			}
			name := strings.Trim(lf[3], ":")
			if _, exist := m.data[lf[1]][name]; !exist {
				value, err := strconv.ParseInt(lf[4], 0, 64)
				if err == nil {
					x := m.data[lf[1]][name]
					x.current = value
					x.last = 0
					m.data[lf[1]][name] = x
				}
			}
		}
	}
	return err
}

func (m *NfsCollector) updateStats() error {
	cmd := exec.Command(m.config.Nfsutils, "-l")
	cmd.Wait()
	buffer, err := cmd.Output()
	if err == nil {
		for _, line := range strings.Split(string(buffer), "\n") {
			lf := strings.Fields(line)
			if len(lf) != 5 {
				continue
			}
			if _, exist := m.data[lf[1]]; !exist {
				m.data[lf[1]] = make(map[string]NfsCollectorData)
			}
			name := strings.Trim(lf[3], ":")
			if _, exist := m.data[lf[1]][name]; exist {
				value, err := strconv.ParseInt(lf[4], 0, 64)
				if err == nil {
					x := m.data[lf[1]][name]
					x.last = x.current
					x.current = value
					m.data[lf[1]][name] = x
				}
			}
		}
	}
	return err
}

func (m *NfsCollector) Init(config json.RawMessage) error {
	var err error
	m.name = "NfsCollector"
	m.setup()

	// Set default mmpmon binary
	m.config.Nfsutils = "/usr/sbin/nfsstat"

	// Read JSON configuration
	if len(config) > 0 {
		err = json.Unmarshal(config, &m.config)
		if err != nil {
			log.Print(err.Error())
			return err
		}
	}
	m.meta = map[string]string{
		"source": m.name,
		"group":  "NFS",
	}
	m.tags = map[string]string{
		"type": "node",
	}
	// Check if mmpmon is in executable search path
	_, err = exec.LookPath(m.config.Nfsutils)
	if err != nil {
		return fmt.Errorf("NfsCollector.Init(): Failed to find nfsstat binary '%s': %v", m.config.Nfsutils, err)
	}
	m.data = make(map[string]map[string]NfsCollectorData)
	m.initStats()
	m.init = true
	return nil
}

func (m *NfsCollector) Read(interval time.Duration, output chan lp.CCMetric) {
	if !m.init {
		return
	}
	timestamp := time.Now()

	m.updateStats()

	for version, metrics := range m.data {
		for name, data := range metrics {
			if _, skip := stringArrayContains(m.config.ExcludeMetrics, name); skip {
				continue
			}
			value := data.current - data.last
			y, err := lp.New(fmt.Sprintf("nfs_%s", name), m.tags, m.meta, map[string]interface{}{"value": value}, timestamp)
			if err == nil {
				y.AddMeta("version", version)
				output <- y
			}
		}
	}
}

func (m *NfsCollector) Close() {
	m.init = false
}