// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0

package provision

import (
	"fmt"
	"os"

	"github.com/absmach/magistrala/channels"
	"github.com/absmach/magistrala/clients"
	"github.com/absmach/magistrala/pkg/errors"
	"github.com/pelletier/go-toml"
)

var errFailedToReadConfig = errors.New("failed to read config file")

// ServiceConf represents service config.
type ServiceConf struct {
	Port        string `toml:"port"          env:"MG_PROVISION_HTTP_PORT"       envDefault:"9016"`
	LogLevel    string `toml:"log_level"     env:"MG_PROVISION_LOG_LEVEL"       envDefault:"info"`
	TLS         bool   `toml:"tls"           env:"MG_PROVISION_ENV_CLIENTS_TLS" envDefault:"false"`
	ServerCert  string `toml:"server_cert"   env:"MG_PROVISION_SERVER_CERT"     envDefault:""`
	ServerKey   string `toml:"server_key"    env:"MG_PROVISION_SERVER_KEY"      envDefault:""`
	ClientsURL  string `toml:"clients_url"   env:"MG_PROVISION_CLIENTS_URL"     envDefault:"http://localhost"`
	ChannelsURL string `toml:"channels_url"  env:"MG_PROVISION_CHANNELS_URL"    envDefault:"http://localhost"`
	UsersURL    string `toml:"users_url"     env:"MG_PROVISION_USERS_URL"       envDefault:"http://localhost"`
	CertsURL    string `toml:"certs_url"     env:"MG_PROVISION_CERTS_URL"       envDefault:"http://localhost"`
	MgEmail     string `toml:"mg_email"      env:"MG_PROVISION_EMAIL"           envDefault:"test@example.com"`
	MgUsername  string `toml:"mg_username"   env:"MG_PROVISION_USERNAME"        envDefault:"user"`
	MgPass      string `toml:"mg_pass"       env:"MG_PROVISION_PASS"            envDefault:"test"`
	MgDomainID  string `toml:"mg_domain_id"  env:"MG_PROVISION_DOMAIN_ID"       envDefault:""`
	MgAPIKey    string `toml:"mg_api_key"    env:"MG_PROVISION_API_KEY"         envDefault:""`
	MgBSURL     string `toml:"mg_bs_url"     env:"MG_PROVISION_BS_SVC_URL"      envDefault:"http://localhost:9000"`
}

// Bootstrap represetns the Bootstrap config.
type Bootstrap struct {
	X509Provision bool               `toml:"x509_provision" env:"MG_PROVISION_X509_PROVISIONING"      envDefault:"false"`
	Provision     bool               `toml:"provision"      env:"MG_PROVISION_BS_CONFIG_PROVISIONING" envDefault:"true"`
	AutoWhiteList bool               `toml:"autowhite_list" env:"MG_PROVISION_BS_AUTO_WHITELIST"      envDefault:"true"`
	ProfileID     string             `toml:"profile_id"     env:"MG_PROVISION_BS_PROFILE_ID"          envDefault:""`
	RenderContext map[string]any     `toml:"render_context,omitempty"`
	Bindings      []BootstrapBinding `toml:"bindings,omitempty"`
	Content       map[string]any     `toml:"content"`
}

// BootstrapBinding maps a bootstrap profile slot to one of the resources
// created by provision.
type BootstrapBinding struct {
	Slot          string `toml:"slot"           json:"slot"`
	Type          string `toml:"type"           json:"type"`
	Name          string `toml:"name"           json:"name,omitempty"`
	MetadataKey   string `toml:"metadata_key"   json:"metadata_key,omitempty"`
	MetadataValue string `toml:"metadata_value" json:"metadata_value,omitempty"`
}

// Gateway represetns the Gateway config.
type Gateway struct {
	Type            string `toml:"type" json:"type"`
	ExternalID      string `toml:"external_id" json:"external_id"`
	ExternalKey     string `toml:"external_key" json:"external_key"`
	CtrlChannelID   string `toml:"ctrl_channel_id" json:"ctrl_channel_id"`
	DataChannelID   string `toml:"data_channel_id" json:"data_channel_id"`
	ExportChannelID string `toml:"export_channel_id" json:"export_channel_id"`
	CfgID           string `toml:"cfg_id" json:"cfg_id"`
}

// Cert represetns the certificate config.
type Cert struct {
	TTL string `json:"ttl" toml:"ttl" env:"MG_PROVISION_CERTS_HOURS_VALID" envDefault:"2400h"`
}

// Config struct of Provision.
type Config struct {
	File          string             `toml:"file"      env:"MG_PROVISION_CONFIG_FILE" envDefault:"config.toml"`
	Server        ServiceConf        `toml:"server"    mapstructure:"server"`
	Bootstrap     Bootstrap          `toml:"bootstrap" mapstructure:"bootstrap"`
	Clients       []clients.Client   `toml:"clients"   mapstructure:"clients"`
	Channels      []channels.Channel `toml:"channels"  mapstructure:"channels"`
	Cert          Cert               `toml:"cert"      mapstructure:"cert"`
	BSContent     string             `env:"MG_PROVISION_BS_CONTENT" envDefault:""`
	SendTelemetry bool               `env:"MG_SEND_TELEMETRY"           envDefault:"true"`
	InstanceID    string             `env:"MG_MQTT_ADAPTER_INSTANCE_ID" envDefault:""`
}

// Save - store config in a file.
func Save(c Config, file string) error {
	if file == "" {
		return errors.ErrEmptyPath
	}

	b, err := toml.Marshal(c)
	if err != nil {
		return errors.Wrap(errFailedToReadConfig, err)
	}
	if err := os.WriteFile(file, b, 0o644); err != nil {
		return fmt.Errorf("Error writing toml: %w", err)
	}

	return nil
}

// Read - retrieve config from a file.
func Read(file string) (Config, error) {
	data, err := os.ReadFile(file)
	if err != nil {
		return Config{}, errors.Wrap(errFailedToReadConfig, err)
	}

	var c Config
	if err := toml.Unmarshal(data, &c); err != nil {
		return Config{}, fmt.Errorf("Error unmarshaling toml: %w", err)
	}
	if len(c.Bootstrap.RenderContext) == 0 {
		c.Bootstrap.RenderContext = nil
	}

	return c, nil
}
