/*
** Copyright (C) 2001-2026 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/

package ceph

import (
	"golang.zabbix.com/sdk/errs"
	"golang.zabbix.com/sdk/metric"
	"golang.zabbix.com/sdk/plugin"
	"golang.zabbix.com/sdk/uri"
)

const (
	keyDf            = "ceph.df.details"
	keyOSD           = "ceph.osd.stats"
	keyOSDDiscovery  = "ceph.osd.discovery"
	keyOSDDump       = "ceph.osd.dump"
	keyPing          = "ceph.ping"
	keyPoolDiscovery = "ceph.pool.discovery"
	keyStatus        = "ceph.status"
)

const (
	cmdDf               = "df"
	cmdPgDump           = "pg dump"
	cmdOSDCrushRuleDump = "osd crush rule dump"
	cmdOSDCrushTree     = "osd crush tree"
	cmdOSDDump          = "osd dump"
	cmdHealth           = "health"
	cmdStatus           = "status"
)

var metricsMeta = map[string]metricMeta{
	keyDf: {
		commands: []command{cmdDf},
		args:     map[string]string{"detail": "detail"},
		handler:  dfHandler,
	},
	keyOSD: {
		commands: []command{cmdPgDump},
		args:     nil,
		handler:  osdHandler,
	},
	keyOSDDiscovery: {
		commands: []command{cmdOSDCrushTree},
		args:     nil,
		handler:  osdDiscoveryHandler,
	},
	keyOSDDump: {
		commands: []command{cmdOSDDump},
		args:     nil,
		handler:  osdDumpHandler,
	},
	keyPing: {
		commands: []command{cmdHealth},
		args:     nil,
		handler:  pingHandler,
	},
	keyPoolDiscovery: {
		commands: []command{cmdOSDDump, cmdOSDCrushRuleDump},
		args:     nil,
		handler:  poolDiscoveryHandler,
	},
	keyStatus: {
		commands: []command{cmdStatus},
		args:     nil,
		handler:  statusHandler,
	},
}

var uriDefaults = &uri.Defaults{Scheme: "https", Port: "8003"}

// Common params: [URI|Session][,User][,ApiKey]
var (
	paramURI = metric.NewConnParam("URI", "URI to connect or session name.").
			WithDefault(uriDefaults.Scheme + "://localhost:" + uriDefaults.Port).WithSession().
			WithValidator(uri.URIValidator{Defaults: uriDefaults, AllowedSchemes: []string{"https"}})
	paramUsername = metric.NewConnParam("User", "Ceph API user.").SetRequired()
	paramAPIKey   = metric.NewConnParam("APIKey", "Ceph API key.").SetRequired()
)

var metrics = metric.MetricSet{
	keyDf: metric.New("Returns information about cluster’s data usage and distribution among pools.",
		[]*metric.Param{paramURI, paramUsername, paramAPIKey}, false),

	keyOSD: metric.New("Returns aggregated and per OSD statistics.",
		[]*metric.Param{paramURI, paramUsername, paramAPIKey}, false),

	keyOSDDiscovery: metric.New("Returns a list of discovered OSDs.",
		[]*metric.Param{paramURI, paramUsername, paramAPIKey}, false),

	keyOSDDump: metric.New("Returns usage thresholds and statuses of OSDs.",
		[]*metric.Param{paramURI, paramUsername, paramAPIKey}, false),

	keyPing: metric.New("Tests if a connection is alive or not.",
		[]*metric.Param{paramURI, paramUsername, paramAPIKey}, false),

	keyPoolDiscovery: metric.New("Returns a list of discovered pools.",
		[]*metric.Param{paramURI, paramUsername, paramAPIKey}, false),

	keyStatus: metric.New("Returns an overall cluster's status.",
		[]*metric.Param{paramURI, paramUsername, paramAPIKey}, false),
}

type command string

// handlerFunc defines an interface must be implemented by handlers.
type handlerFunc func(data map[command][]byte) (res interface{}, err error)

type metricMeta struct {
	commands []command
	args     map[string]string
	handler  handlerFunc
}

func init() {
	err := plugin.RegisterMetrics(&impl, pluginName, metrics.List()...)
	if err != nil {
		panic(errs.Wrap(err, "failed to register metrics"))
	}
}

// handle runs metric's handler.
func (m *metricMeta) handle(data map[command][]byte) (res interface{}, err error) {
	return m.handler(data)
}
