package ceph

import (
	"fmt"
	"os"
	"path/filepath"
	"text/template"
)

// ConfigWriter is an interface for writing config files
type ConfigWriter interface {
	WriteConfig(any) error
}

// Config struct
type Config struct {
	configTemplate *template.Template
	configDir      string
	configFile     string
}

// GetPath returns the path to the config file
func (c *Config) GetPath() string {
	return filepath.Join(c.configDir, c.configFile)
}

// WriteConfig writes the configuration file given a data bag and a filemode
func (c *Config) WriteConfig(data map[string]any, mode int) error {
	fd, err := os.OpenFile(c.GetPath(), os.O_CREATE|os.O_TRUNC|os.O_RDWR, os.FileMode(mode))
	if err != nil {
		return fmt.Errorf("Couldn't write %s: %w", c.configFile, err)
	}
	defer fd.Close()

	err = c.configTemplate.Execute(fd, data)
	if err != nil {
		return fmt.Errorf("Couldn't render %s: %w", c.configFile, err)
	}
	return nil
}

// newRadosGWConfig creates a new radosgw config file
func newRadosGWConfig(configDir string) *Config {
	return &Config{
		configTemplate: template.Must(template.New("radosgwConfig").Parse(`# Generated by MicroCeph, DO NOT EDIT.
[global]
mon host = {{.monitors}}
run dir = {{.runDir}}
auth allow insecure global id reclaim = false

[client.radosgw.gateway]
rgw init timeout = 1200
rgw frontends = beast{{if or (ne .rgwPort 0) (not .sslCertificatePath) (not .sslPrivateKeyPath)}} port={{.rgwPort}}{{end}}{{if and .sslCertificatePath .sslPrivateKeyPath}} ssl_port={{.sslPort}} ssl_certificate={{.sslCertificatePath}} ssl_private_key={{.sslPrivateKeyPath}}{{end}}
`)),
		configFile: "radosgw.conf",
		configDir:  configDir,
	}
}

// newGaneshaConfig creates a new NFS Ganesha config file
func newGaneshaConfig(configDir string) *Config {
	return &Config{
		configTemplate: template.Must(template.New("ganeshaConfig").Parse(`# Generated by MicroCeph, DO NOT EDIT.

NFS_CORE_PARAM {
	Enable_NLM = false;
	Enable_RQUOTA = false;
	Protocols = 4;
	Bind_Addr = {{.bindAddr}};
	NFS_Port = {{.bindPort}};
	Plugins_Dir = "{{.snapDir}}/lib/x86_64-linux-gnu/ganesha";
}

NFSv4 {
	Delegations = false;
	RecoveryBackend = 'rados_cluster';
	Minor_Versions = {{.minorVersions}};
	UseGetpwnam = true;
}

# The libcephfs client will aggressively cache information while it
# can, so there is little benefit to ganesha actively caching the same
# objects. Doing so can also hurt cache coherency. Here, we disable
# as much attribute and directory caching as we can.
MDCACHE {
        # Size the dirent cache down as small as possible.
        Dir_Chunk = 0;
}

NFS_KRB5 {
	CCacheDir = "{{.runDir}}/ganesha";
}

RADOS_KV {
	ceph_conf = "{{.confDir}}/ceph.conf";
	UserId = "{{.userID}}";
	pool = ".nfs";
	namespace = "{{.clusterID}}";
}

RADOS_URLS {
	ceph_conf = "{{.confDir}}/ceph.conf";
	UserId = "{{.userID}}";
	watch_url = "rados://.nfs/{{.clusterID}}/conf-nfs.{{.clusterID}}";
}

LOG {
	Components {
		ALL = EVENT;
	}
}

%url    rados://.nfs/{{.clusterID}}/conf-nfs.{{.clusterID}}
`)),
		configFile: "ganesha.conf",
		configDir:  configDir,
	}
}

// newGaneshaCephConfig creates a new ceph configuration file for ganesha with given name.
func newGaneshaCephConfig(configDir string) *Config {
	return &Config{
		configTemplate: template.Must(template.New("ganeshaCephConf").Parse(`# Generated by MicroCeph, DO NOT EDIT.

[global]
auth supported = cephx
mon host = {{.monitors}}
keyring = {{.confDir}}/keyring
`)),
		configFile: "ceph.conf",
		configDir:  configDir,
	}
}
