// Copyright (C) 2019 Nicola Murino
//
// 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/>.

//go:build !nometrics
// +build !nometrics

// Package metric provides Prometheus metrics support
package metric

import (
	"github.com/go-chi/chi/v5"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/client_golang/prometheus/promhttp"

	"github.com/drakkan/sftpgo/v2/internal/version"
)

const (
	loginMethodPublicKey            = "publickey"
	loginMethodKeyboardInteractive  = "keyboard-interactive"
	loginMethodKeyAndPassword       = "publickey+password"
	loginMethodKeyAndKeyboardInt    = "publickey+keyboard-interactive"
	loginMethodTLSCertificate       = "TLSCertificate"
	loginMethodTLSCertificateAndPwd = "TLSCertificate+password"
	loginMethodIDP                  = "IDP"
)

func init() {
	version.AddFeature("+metrics")
}

var (
	// dataproviderAvailability is the metric that reports the availability for the configured data provider
	dataproviderAvailability = promauto.NewGauge(prometheus.GaugeOpts{
		Name: "sftpgo_dataprovider_availability",
		Help: "Availability for the configured data provider, 1 means OK, 0 KO",
	})

	// activeConnections is the metric that reports the total number of active connections
	activeConnections = promauto.NewGauge(prometheus.GaugeOpts{
		Name: "sftpgo_active_connections",
		Help: "Total number of logged in users",
	})

	// totalUploads is the metric that reports the total number of successful uploads
	totalUploads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_uploads_total",
		Help: "The total number of successful uploads",
	})

	// totalDownloads is the metric that reports the total number of successful downloads
	totalDownloads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_downloads_total",
		Help: "The total number of successful downloads",
	})

	// totalUploadErrors is the metric that reports the total number of upload errors
	totalUploadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_upload_errors_total",
		Help: "The total number of upload errors",
	})

	// totalDownloadErrors is the metric that reports the total number of download errors
	totalDownloadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_download_errors_total",
		Help: "The total number of download errors",
	})

	// totalUploadSize is the metric that reports the total uploads size as bytes
	totalUploadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_upload_size",
		Help: "The total upload size as bytes, partial uploads are included",
	})

	// totalDownloadSize is the metric that reports the total downloads size as bytes
	totalDownloadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_download_size",
		Help: "The total download size as bytes, partial downloads are included",
	})

	// totalSSHCommands is the metric that reports the total number of executed SSH commands
	totalSSHCommands = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_ssh_commands_total",
		Help: "The total number of executed SSH commands",
	})

	// totalSSHCommandErrors is the metric that reports the total number of SSH command errors
	totalSSHCommandErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_ssh_command_errors_total",
		Help: "The total number of SSH command errors",
	})

	// totalLoginAttempts is the metric that reports the total number of login attempts
	totalLoginAttempts = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_login_attempts_total",
		Help: "The total number of login attempts",
	})

	// totalNoAuthTried is te metric that reports the total number of clients disconnected
	// for inactivity before trying to login
	totalNoAuthTried = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_no_auth_total",
		Help: "The total number of clients disconnected for inactivity before trying to login",
	})

	// totalLoginOK is the metric that reports the total number of successful logins
	totalLoginOK = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_login_ok_total",
		Help: "The total number of successful logins",
	})

	// totalLoginFailed is the metric that reports the total number of failed logins
	totalLoginFailed = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_login_ko_total",
		Help: "The total number of failed logins",
	})

	// totalPasswordLoginAttempts is the metric that reports the total number of login attempts
	// using a password
	totalPasswordLoginAttempts = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_password_login_attempts_total",
		Help: "The total number of login attempts using a password",
	})

	// totalPasswordLoginOK is the metric that reports the total number of successful logins
	// using a password
	totalPasswordLoginOK = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_password_login_ok_total",
		Help: "The total number of successful logins using a password",
	})

	// totalPasswordLoginFailed is the metric that reports the total number of failed logins
	// using a password
	totalPasswordLoginFailed = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_password_login_ko_total",
		Help: "The total number of failed logins using a password",
	})

	// totalKeyLoginAttempts is the metric that reports the total number of login attempts
	// using a public key
	totalKeyLoginAttempts = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_public_key_login_attempts_total",
		Help: "The total number of login attempts using a public key",
	})

	// totalKeyLoginOK is the metric that reports the total number of successful logins
	// using a public key
	totalKeyLoginOK = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_public_key_login_ok_total",
		Help: "The total number of successful logins using a public key",
	})

	// totalKeyLoginFailed is the metric that reports the total number of failed logins
	// using a public key
	totalKeyLoginFailed = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_public_key_login_ko_total",
		Help: "The total number of failed logins using a public key",
	})

	// totalTLSCertLoginAttempts is the metric that reports the total number of login attempts
	// using a TLS certificate
	totalTLSCertLoginAttempts = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_tls_cert_login_attempts_total",
		Help: "The total number of login attempts using a TLS certificate",
	})

	// totalTLSCertLoginOK is the metric that reports the total number of successful logins
	// using a TLS certificate
	totalTLSCertLoginOK = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_tls_cert_login_ok_total",
		Help: "The total number of successful logins using a TLS certificate",
	})

	// totalTLSCertLoginFailed is the metric that reports the total number of failed logins
	// using a TLS certificate
	totalTLSCertLoginFailed = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_tls_cert_login_ko_total",
		Help: "The total number of failed logins using a TLS certificate",
	})

	// totalTLSCertAndPwdLoginAttempts is the metric that reports the total number of login attempts
	// using a TLS certificate+password
	totalTLSCertAndPwdLoginAttempts = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_tls_cert_and_pwd_login_attempts_total",
		Help: "The total number of login attempts using a TLS certificate+password",
	})

	// totalTLSCertLoginOK is the metric that reports the total number of successful logins
	// using a TLS certificate+password
	totalTLSCertAndPwdLoginOK = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_tls_cert_and_pwd_login_ok_total",
		Help: "The total number of successful logins using a TLS certificate+password",
	})

	// totalTLSCertAndPwdLoginFailed is the metric that reports the total number of failed logins
	// using a TLS certificate+password
	totalTLSCertAndPwdLoginFailed = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_tls_cert_and_pwd_login_ko_total",
		Help: "The total number of failed logins using a TLS certificate+password",
	})

	// totalInteractiveLoginAttempts is the metric that reports the total number of login attempts
	// using keyboard interactive authentication
	totalInteractiveLoginAttempts = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_keyboard_interactive_login_attempts_total",
		Help: "The total number of login attempts using keyboard interactive authentication",
	})

	// totalInteractiveLoginOK is the metric that reports the total number of successful logins
	// using keyboard interactive authentication
	totalInteractiveLoginOK = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_keyboard_interactive_login_ok_total",
		Help: "The total number of successful logins using keyboard interactive authentication",
	})

	// totalInteractiveLoginFailed is the metric that reports the total number of failed logins
	// using keyboard interactive authentication
	totalInteractiveLoginFailed = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_keyboard_interactive_login_ko_total",
		Help: "The total number of failed logins using keyboard interactive authentication",
	})

	// totalKeyAndPasswordLoginAttempts is the metric that reports the total number of
	// login attempts using public key + password multi steps auth
	totalKeyAndPasswordLoginAttempts = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_key_and_password_login_attempts_total",
		Help: "The total number of login attempts using public key + password",
	})

	// totalKeyAndPasswordLoginOK is the metric that reports the total number of
	// successful logins using public key + password multi steps auth
	totalKeyAndPasswordLoginOK = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_key_and_password_login_ok_total",
		Help: "The total number of successful logins using public key + password",
	})

	// totalKeyAndPasswordLoginFailed is the metric that reports the total number of
	// failed logins using public key + password multi steps auth
	totalKeyAndPasswordLoginFailed = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_key_and_password_login_ko_total",
		Help: "The total number of failed logins using  public key + password",
	})

	// totalKeyAndKeyIntLoginAttempts is the metric that reports the total number of
	// login attempts using public key + keyboard interactive multi steps auth
	totalKeyAndKeyIntLoginAttempts = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_key_and_keyboard_int_login_attempts_total",
		Help: "The total number of login attempts using public key + keyboard interactive",
	})

	// totalKeyAndKeyIntLoginOK is the metric that reports the total number of
	// successful logins using public key + keyboard interactive multi steps auth
	totalKeyAndKeyIntLoginOK = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_key_and_keyboard_int_login_ok_total",
		Help: "The total number of successful logins using public key + keyboard interactive",
	})

	// totalKeyAndKeyIntLoginFailed is the metric that reports the total number of
	// failed logins using public key + keyboard interactive multi steps auth
	totalKeyAndKeyIntLoginFailed = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_key_and_keyboard_int_login_ko_total",
		Help: "The total number of failed logins using  public key + keyboard interactive",
	})

	// totalIDPLoginAttempts is the metric that reports the total number of
	// login attempts using identity providers
	totalIDPLoginAttempts = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_idp_login_attempts_total",
		Help: "The total number of login attempts using Identity Providers",
	})

	// totalIDPLoginOK is the metric that reports the total number of
	// successful logins using identity providers
	totalIDPLoginOK = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_idp_login_ok_total",
		Help: "The total number of successful logins using Identity Providers",
	})

	// totalIDPLoginFailed is the metric that reports the total number of
	// failed logins using identity providers
	totalIDPLoginFailed = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_idp_login_ko_total",
		Help: "The total number of failed logins using  Identity Providers",
	})

	totalHTTPRequests = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_http_req_total",
		Help: "The total number of HTTP requests served",
	})

	totalHTTPOK = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_http_req_ok_total",
		Help: "The total number of HTTP requests served with 2xx status code",
	})

	totalHTTPClientErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_http_client_errors_total",
		Help: "The total number of HTTP requests served with 4xx status code",
	})

	totalHTTPServerErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_http_server_errors_total",
		Help: "The total number of HTTP requests served with 5xx status code",
	})

	// totalS3Uploads is the metric that reports the total number of successful S3 uploads
	totalS3Uploads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_uploads_total",
		Help: "The total number of successful S3 uploads",
	})

	// totalS3Downloads is the metric that reports the total number of successful S3 downloads
	totalS3Downloads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_downloads_total",
		Help: "The total number of successful S3 downloads",
	})

	// totalS3UploadErrors is the metric that reports the total number of S3 upload errors
	totalS3UploadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_upload_errors_total",
		Help: "The total number of S3 upload errors",
	})

	// totalS3DownloadErrors is the metric that reports the total number of S3 download errors
	totalS3DownloadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_download_errors_total",
		Help: "The total number of S3 download errors",
	})

	// totalS3UploadSize is the metric that reports the total S3 uploads size as bytes
	totalS3UploadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_upload_size",
		Help: "The total S3 upload size as bytes, partial uploads are included",
	})

	// totalS3DownloadSize is the metric that reports the total S3 downloads size as bytes
	totalS3DownloadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_download_size",
		Help: "The total S3 download size as bytes, partial downloads are included",
	})

	// totalS3ListObjects is the metric that reports the total successful S3 list objects requests
	totalS3ListObjects = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_list_objects",
		Help: "The total number of successful S3 list objects requests",
	})

	// totalS3CopyObject is the metric that reports the total successful S3 copy object requests
	totalS3CopyObject = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_copy_object",
		Help: "The total number of successful S3 copy object requests",
	})

	// totalS3DeleteObject is the metric that reports the total successful S3 delete object requests
	totalS3DeleteObject = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_delete_object",
		Help: "The total number of successful S3 delete object requests",
	})

	// totalS3ListObjectsError is the metric that reports the total S3 list objects errors
	totalS3ListObjectsErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_list_objects_errors",
		Help: "The total number of S3 list objects errors",
	})

	// totalS3CopyObjectErrors is the metric that reports the total S3 copy object errors
	totalS3CopyObjectErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_copy_object_errors",
		Help: "The total number of S3 copy object errors",
	})

	// totalS3DeleteObjectErrors is the metric that reports the total S3 delete object errors
	totalS3DeleteObjectErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_delete_object_errors",
		Help: "The total number of S3 delete object errors",
	})

	// totalS3HeadObject is the metric that reports the total successful S3 head object requests
	totalS3HeadObject = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_head_object",
		Help: "The total number of successful S3 head object requests",
	})

	// totalS3HeadObjectErrors is the metric that reports the total S3 head object errors
	totalS3HeadObjectErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_s3_head_object_errors",
		Help: "The total number of S3 head object errors",
	})

	// totalGCSUploads is the metric that reports the total number of successful GCS uploads
	totalGCSUploads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_uploads_total",
		Help: "The total number of successful GCS uploads",
	})

	// totalGCSDownloads is the metric that reports the total number of successful GCS downloads
	totalGCSDownloads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_downloads_total",
		Help: "The total number of successful GCS downloads",
	})

	// totalGCSUploadErrors is the metric that reports the total number of GCS upload errors
	totalGCSUploadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_upload_errors_total",
		Help: "The total number of GCS upload errors",
	})

	// totalGCSDownloadErrors is the metric that reports the total number of GCS download errors
	totalGCSDownloadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_download_errors_total",
		Help: "The total number of GCS download errors",
	})

	// totalGCSUploadSize is the metric that reports the total GCS uploads size as bytes
	totalGCSUploadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_upload_size",
		Help: "The total GCS upload size as bytes, partial uploads are included",
	})

	// totalGCSDownloadSize is the metric that reports the total GCS downloads size as bytes
	totalGCSDownloadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_download_size",
		Help: "The total GCS download size as bytes, partial downloads are included",
	})

	// totalGCSListObjects is the metric that reports the total successful GCS list objects requests
	totalGCSListObjects = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_list_objects",
		Help: "The total number of successful GCS list objects requests",
	})

	// totalGCSCopyObject is the metric that reports the total successful GCS copy object requests
	totalGCSCopyObject = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_copy_object",
		Help: "The total number of successful GCS copy object requests",
	})

	// totalGCSDeleteObject is the metric that reports the total successful GCS delete object requests
	totalGCSDeleteObject = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_delete_object",
		Help: "The total number of successful GCS delete object requests",
	})

	// totalGCSListObjectsError is the metric that reports the total GCS list objects errors
	totalGCSListObjectsErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_list_objects_errors",
		Help: "The total number of GCS list objects errors",
	})

	// totalGCSCopyObjectErrors is the metric that reports the total GCS copy object errors
	totalGCSCopyObjectErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_copy_object_errors",
		Help: "The total number of GCS copy object errors",
	})

	// totalGCSDeleteObjectErrors is the metric that reports the total GCS delete object errors
	totalGCSDeleteObjectErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_delete_object_errors",
		Help: "The total number of GCS delete object errors",
	})

	// totalGCSHeadObject is the metric that reports the total successful GCS head object requests
	totalGCSHeadObject = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_head_object",
		Help: "The total number of successful GCS head object requests",
	})

	// totalGCSHeadObjectErrors is the metric that reports the total GCS head object errors
	totalGCSHeadObjectErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_gcs_head_object_errors",
		Help: "The total number of GCS head object errors",
	})

	// totalAZUploads is the metric that reports the total number of successful Azure uploads
	totalAZUploads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_uploads_total",
		Help: "The total number of successful Azure uploads",
	})

	// totalAZDownloads is the metric that reports the total number of successful Azure downloads
	totalAZDownloads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_downloads_total",
		Help: "The total number of successful Azure downloads",
	})

	// totalAZUploadErrors is the metric that reports the total number of Azure upload errors
	totalAZUploadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_upload_errors_total",
		Help: "The total number of Azure upload errors",
	})

	// totalAZDownloadErrors is the metric that reports the total number of Azure download errors
	totalAZDownloadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_download_errors_total",
		Help: "The total number of Azure download errors",
	})

	// totalAZUploadSize is the metric that reports the total Azure uploads size as bytes
	totalAZUploadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_upload_size",
		Help: "The total Azure upload size as bytes, partial uploads are included",
	})

	// totalAZDownloadSize is the metric that reports the total Azure downloads size as bytes
	totalAZDownloadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_download_size",
		Help: "The total Azure download size as bytes, partial downloads are included",
	})

	// totalAZListObjects is the metric that reports the total successful Azure list objects requests
	totalAZListObjects = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_list_objects",
		Help: "The total number of successful Azure list objects requests",
	})

	// totalAZCopyObject is the metric that reports the total successful Azure copy object requests
	totalAZCopyObject = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_copy_object",
		Help: "The total number of successful Azure copy object requests",
	})

	// totalAZDeleteObject is the metric that reports the total successful Azure delete object requests
	totalAZDeleteObject = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_delete_object",
		Help: "The total number of successful Azure delete object requests",
	})

	// totalAZListObjectsError is the metric that reports the total Azure list objects errors
	totalAZListObjectsErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_list_objects_errors",
		Help: "The total number of Azure list objects errors",
	})

	// totalAZCopyObjectErrors is the metric that reports the total Azure copy object errors
	totalAZCopyObjectErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_copy_object_errors",
		Help: "The total number of Azure copy object errors",
	})

	// totalAZDeleteObjectErrors is the metric that reports the total Azure delete object errors
	totalAZDeleteObjectErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_delete_object_errors",
		Help: "The total number of Azure delete object errors",
	})

	// totalAZHeadObject is the metric that reports the total successful Azure head object requests
	totalAZHeadObject = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_head_object",
		Help: "The total number of successful Azure head object requests",
	})

	// totalAZHeadObjectErrors is the metric that reports the total Azure head object errors
	totalAZHeadObjectErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_az_head_object_errors",
		Help: "The total number of Azure head object errors",
	})

	// totalSFTPFsUploads is the metric that reports the total number of successful SFTPFs uploads
	totalSFTPFsUploads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_sftpfs_uploads_total",
		Help: "The total number of successful SFTPFs uploads",
	})

	// totalSFTPFsDownloads is the metric that reports the total number of successful SFTPFs downloads
	totalSFTPFsDownloads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_sftpfs_downloads_total",
		Help: "The total number of successful SFTPFs downloads",
	})

	// totalSFTPFsUploadErrors is the metric that reports the total number of SFTPFs upload errors
	totalSFTPFsUploadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_sftpfs_upload_errors_total",
		Help: "The total number of SFTPFs upload errors",
	})

	// totalSFTPFsDownloadErrors is the metric that reports the total number of SFTPFs download errors
	totalSFTPFsDownloadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_sftpfs_download_errors_total",
		Help: "The total number of SFTPFs download errors",
	})

	// totalSFTPFsUploadSize is the metric that reports the total SFTPFs uploads size as bytes
	totalSFTPFsUploadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_sftpfs_upload_size",
		Help: "The total SFTPFs upload size as bytes, partial uploads are included",
	})

	// totalSFTPFsDownloadSize is the metric that reports the total SFTPFs downloads size as bytes
	totalSFTPFsDownloadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_sftpfs_download_size",
		Help: "The total SFTPFs download size as bytes, partial downloads are included",
	})

	// totalHTTPFsUploads is the metric that reports the total number of successful HTTPFs uploads
	totalHTTPFsUploads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_httpfs_uploads_total",
		Help: "The total number of successful HTTPFs uploads",
	})

	// totalHTTPFsDownloads is the metric that reports the total number of successful HTTPFs downloads
	totalHTTPFsDownloads = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_httpfs_downloads_total",
		Help: "The total number of successful HTTPFs downloads",
	})

	// totalHTTPFsUploadErrors is the metric that reports the total number of HTTPFs upload errors
	totalHTTPFsUploadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_httpfs_upload_errors_total",
		Help: "The total number of HTTPFs upload errors",
	})

	// totalHTTPFsDownloadErrors is the metric that reports the total number of HTTPFs download errors
	totalHTTPFsDownloadErrors = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_httpfs_download_errors_total",
		Help: "The total number of HTTPFs download errors",
	})

	// totalHTTPFsUploadSize is the metric that reports the total HTTPFs uploads size as bytes
	totalHTTPFsUploadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_httpfs_upload_size",
		Help: "The total HTTPFs upload size as bytes, partial uploads are included",
	})

	// totalHTTPFsDownloadSize is the metric that reports the total HTTPFs downloads size as bytes
	totalHTTPFsDownloadSize = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sftpgo_httpfs_download_size",
		Help: "The total HTTPFs download size as bytes, partial downloads are included",
	})
)

// AddMetricsEndpoint publishes metrics to the specified endpoint
func AddMetricsEndpoint(metricsPath string, handler chi.Router) {
	handler.Handle(metricsPath, promhttp.Handler())
}

// TransferCompleted updates metrics after an upload or a download
func TransferCompleted(bytesSent, bytesReceived int64, transferKind int, err error, isSFTPFs bool) {
	if transferKind == 0 {
		// upload
		if err == nil {
			totalUploads.Inc()
		} else {
			totalUploadErrors.Inc()
		}
	} else {
		// download
		if err == nil {
			totalDownloads.Inc()
		} else {
			totalDownloadErrors.Inc()
		}
	}
	if bytesReceived > 0 {
		totalUploadSize.Add(float64(bytesReceived))
	}
	if bytesSent > 0 {
		totalDownloadSize.Add(float64(bytesSent))
	}
	if isSFTPFs {
		sftpFsTransferCompleted(bytesSent, bytesReceived, transferKind, err)
	}
}

// S3TransferCompleted updates metrics after an S3 upload or a download
func S3TransferCompleted(bytes int64, transferKind int, err error) {
	if transferKind == 0 {
		// upload
		if err == nil {
			totalS3Uploads.Inc()
		} else {
			totalS3UploadErrors.Inc()
		}
		totalS3UploadSize.Add(float64(bytes))
	} else {
		// download
		if err == nil {
			totalS3Downloads.Inc()
		} else {
			totalS3DownloadErrors.Inc()
		}
		totalS3DownloadSize.Add(float64(bytes))
	}
}

// S3ListObjectsCompleted updates metrics after an S3 list objects request terminates
func S3ListObjectsCompleted(err error) {
	if err == nil {
		totalS3ListObjects.Inc()
	} else {
		totalS3ListObjectsErrors.Inc()
	}
}

// S3CopyObjectCompleted updates metrics after an S3 copy object request terminates
func S3CopyObjectCompleted(err error) {
	if err == nil {
		totalS3CopyObject.Inc()
	} else {
		totalS3CopyObjectErrors.Inc()
	}
}

// S3DeleteObjectCompleted updates metrics after an S3 delete object request terminates
func S3DeleteObjectCompleted(err error) {
	if err == nil {
		totalS3DeleteObject.Inc()
	} else {
		totalS3DeleteObjectErrors.Inc()
	}
}

// S3HeadObjectCompleted updates metrics after a S3 head object request terminates
func S3HeadObjectCompleted(err error) {
	if err == nil {
		totalS3HeadObject.Inc()
	} else {
		totalS3HeadObjectErrors.Inc()
	}
}

// GCSTransferCompleted updates metrics after a GCS upload or a download
func GCSTransferCompleted(bytes int64, transferKind int, err error) {
	if transferKind == 0 {
		// upload
		if err == nil {
			totalGCSUploads.Inc()
		} else {
			totalGCSUploadErrors.Inc()
		}
		totalGCSUploadSize.Add(float64(bytes))
	} else {
		// download
		if err == nil {
			totalGCSDownloads.Inc()
		} else {
			totalGCSDownloadErrors.Inc()
		}
		totalGCSDownloadSize.Add(float64(bytes))
	}
}

// GCSListObjectsCompleted updates metrics after a GCS list objects request terminates
func GCSListObjectsCompleted(err error) {
	if err == nil {
		totalGCSListObjects.Inc()
	} else {
		totalGCSListObjectsErrors.Inc()
	}
}

// GCSCopyObjectCompleted updates metrics after a GCS copy object request terminates
func GCSCopyObjectCompleted(err error) {
	if err == nil {
		totalGCSCopyObject.Inc()
	} else {
		totalGCSCopyObjectErrors.Inc()
	}
}

// GCSDeleteObjectCompleted updates metrics after a GCS delete object request terminates
func GCSDeleteObjectCompleted(err error) {
	if err == nil {
		totalGCSDeleteObject.Inc()
	} else {
		totalGCSDeleteObjectErrors.Inc()
	}
}

// GCSHeadObjectCompleted updates metrics after a GCS head object request terminates
func GCSHeadObjectCompleted(err error) {
	if err == nil {
		totalGCSHeadObject.Inc()
	} else {
		totalGCSHeadObjectErrors.Inc()
	}
}

// AZTransferCompleted updates metrics after a Azure upload or a download
func AZTransferCompleted(bytes int64, transferKind int, err error) {
	if transferKind == 0 {
		// upload
		if err == nil {
			totalAZUploads.Inc()
		} else {
			totalAZUploadErrors.Inc()
		}
		totalAZUploadSize.Add(float64(bytes))
	} else {
		// download
		if err == nil {
			totalAZDownloads.Inc()
		} else {
			totalAZDownloadErrors.Inc()
		}
		totalAZDownloadSize.Add(float64(bytes))
	}
}

// AZListObjectsCompleted updates metrics after a Azure list objects request terminates
func AZListObjectsCompleted(err error) {
	if err == nil {
		totalAZListObjects.Inc()
	} else {
		totalAZListObjectsErrors.Inc()
	}
}

// AZCopyObjectCompleted updates metrics after a Azure copy object request terminates
func AZCopyObjectCompleted(err error) {
	if err == nil {
		totalAZCopyObject.Inc()
	} else {
		totalAZCopyObjectErrors.Inc()
	}
}

// AZDeleteObjectCompleted updates metrics after a Azure delete object request terminates
func AZDeleteObjectCompleted(err error) {
	if err == nil {
		totalAZDeleteObject.Inc()
	} else {
		totalAZDeleteObjectErrors.Inc()
	}
}

// AZHeadObjectCompleted updates metrics after a Azure head object request terminates
func AZHeadObjectCompleted(err error) {
	if err == nil {
		totalAZHeadObject.Inc()
	} else {
		totalAZHeadObjectErrors.Inc()
	}
}

// sftpFsTransferCompleted updates metrics after an SFTPFs upload or a download
func sftpFsTransferCompleted(bytesSent, bytesReceived int64, transferKind int, err error) {
	if transferKind == 0 {
		// upload
		if err == nil {
			totalSFTPFsUploads.Inc()
		} else {
			totalSFTPFsUploadErrors.Inc()
		}
	} else {
		// download
		if err == nil {
			totalSFTPFsDownloads.Inc()
		} else {
			totalSFTPFsDownloadErrors.Inc()
		}
	}
	if bytesReceived > 0 {
		totalSFTPFsUploadSize.Add(float64(bytesReceived))
	}
	if bytesSent > 0 {
		totalSFTPFsDownloadSize.Add(float64(bytesSent))
	}
}

// HTTPFsTransferCompleted updates metrics after an HTTPFs upload or a download
func HTTPFsTransferCompleted(bytes int64, transferKind int, err error) {
	if transferKind == 0 {
		// upload
		if err == nil {
			totalHTTPFsUploads.Inc()
		} else {
			totalHTTPFsUploadErrors.Inc()
		}
		totalHTTPFsUploadSize.Add(float64(bytes))
	} else {
		// download
		if err == nil {
			totalHTTPFsDownloads.Inc()
		} else {
			totalHTTPFsDownloadErrors.Inc()
		}
		totalHTTPFsDownloadSize.Add(float64(bytes))
	}
}

// SSHCommandCompleted update metrics after an SSH command terminates
func SSHCommandCompleted(err error) {
	if err == nil {
		totalSSHCommands.Inc()
	} else {
		totalSSHCommandErrors.Inc()
	}
}

// UpdateDataProviderAvailability updates the metric for the data provider availability
func UpdateDataProviderAvailability(err error) {
	if err == nil {
		dataproviderAvailability.Set(1)
	} else {
		dataproviderAvailability.Set(0)
	}
}

// AddLoginAttempt increments the metrics for login attempts
func AddLoginAttempt(authMethod string) {
	totalLoginAttempts.Inc()
	switch authMethod {
	case loginMethodPublicKey:
		totalKeyLoginAttempts.Inc()
	case loginMethodKeyboardInteractive:
		totalInteractiveLoginAttempts.Inc()
	case loginMethodKeyAndPassword:
		totalKeyAndPasswordLoginAttempts.Inc()
	case loginMethodKeyAndKeyboardInt:
		totalKeyAndKeyIntLoginAttempts.Inc()
	case loginMethodTLSCertificate:
		totalTLSCertLoginAttempts.Inc()
	case loginMethodTLSCertificateAndPwd:
		totalTLSCertAndPwdLoginAttempts.Inc()
	case loginMethodIDP:
		totalIDPLoginAttempts.Inc()
	default:
		totalPasswordLoginAttempts.Inc()
	}
}

func incLoginOK(authMethod string) {
	totalLoginOK.Inc()
	switch authMethod {
	case loginMethodPublicKey:
		totalKeyLoginOK.Inc()
	case loginMethodKeyboardInteractive:
		totalInteractiveLoginOK.Inc()
	case loginMethodKeyAndPassword:
		totalKeyAndPasswordLoginOK.Inc()
	case loginMethodKeyAndKeyboardInt:
		totalKeyAndKeyIntLoginOK.Inc()
	case loginMethodTLSCertificate:
		totalTLSCertLoginOK.Inc()
	case loginMethodTLSCertificateAndPwd:
		totalTLSCertAndPwdLoginOK.Inc()
	case loginMethodIDP:
		totalIDPLoginOK.Inc()
	default:
		totalPasswordLoginOK.Inc()
	}
}

func incLoginFailed(authMethod string) {
	totalLoginFailed.Inc()
	switch authMethod {
	case loginMethodPublicKey:
		totalKeyLoginFailed.Inc()
	case loginMethodKeyboardInteractive:
		totalInteractiveLoginFailed.Inc()
	case loginMethodKeyAndPassword:
		totalKeyAndPasswordLoginFailed.Inc()
	case loginMethodKeyAndKeyboardInt:
		totalKeyAndKeyIntLoginFailed.Inc()
	case loginMethodTLSCertificate:
		totalTLSCertLoginFailed.Inc()
	case loginMethodTLSCertificateAndPwd:
		totalTLSCertAndPwdLoginFailed.Inc()
	case loginMethodIDP:
		totalIDPLoginFailed.Inc()
	default:
		totalPasswordLoginFailed.Inc()
	}
}

// AddLoginResult increments the metrics for login results
func AddLoginResult(authMethod string, err error) {
	if err == nil {
		incLoginOK(authMethod)
	} else {
		incLoginFailed(authMethod)
	}
}

// AddNoAuthTried increments the metric for clients disconnected
// for inactivity before trying to login
func AddNoAuthTried() {
	totalNoAuthTried.Inc()
}

// HTTPRequestServed increments the metrics for HTTP requests
func HTTPRequestServed(status int) {
	totalHTTPRequests.Inc()
	if status >= 200 && status < 300 {
		totalHTTPOK.Inc()
	} else if status >= 400 && status < 500 {
		totalHTTPClientErrors.Inc()
	} else if status >= 500 {
		totalHTTPServerErrors.Inc()
	}
}

// UpdateActiveConnectionsSize sets the metric for active connections
func UpdateActiveConnectionsSize(size int) {
	activeConnections.Set(float64(size))
}
