package config

import (
	"fmt"
	"os"
	"time"

	"github.com/a8m/envsubst"
	"github.com/sirupsen/logrus"

	"gopkg.in/yaml.v2"
)

type Config struct {
	Core struct {
		// AdminUser defines the default administrator account that will be created
		AdminUser     string `yaml:"admin_user"`
		AdminPassword string `yaml:"admin_password"`

		EditableKeys                bool `yaml:"editable_keys"`
		CreateDefaultPeer           bool `yaml:"create_default_peer"`
		CreateDefaultPeerOnCreation bool `yaml:"create_default_peer_on_creation"`
		ReEnablePeerAfterUserEnable bool `yaml:"re_enable_peer_after_user_enable"`
		DeletePeerAfterUserDeleted  bool `yaml:"delete_peer_after_user_deleted"`
		SelfProvisioningAllowed     bool `yaml:"self_provisioning_allowed"`
		ImportExisting              bool `yaml:"import_existing"`
		RestoreState                bool `yaml:"restore_state"`
	} `yaml:"core"`

	Advanced struct {
		LogLevel            string        `yaml:"log_level"`
		LogPretty           bool          `yaml:"log_pretty"`
		LogJson             bool          `yaml:"log_json"`
		StartListenPort     int           `yaml:"start_listen_port"`
		StartCidrV4         string        `yaml:"start_cidr_v4"`
		StartCidrV6         string        `yaml:"start_cidr_v6"`
		UseIpV6             bool          `yaml:"use_ip_v6"`
		ConfigStoragePath   string        `yaml:"config_storage_path"` // keep empty to disable config export to file
		ExpiryCheckInterval time.Duration `yaml:"expiry_check_interval"`
		RulePrioOffset      int           `yaml:"rule_prio_offset"`
		RouteTableOffset    int           `yaml:"route_table_offset"`
		ApiAdminOnly        bool          `yaml:"api_admin_only"` // if true, only admin users can access the API
	} `yaml:"advanced"`

	Statistics struct {
		UsePingChecks          bool          `yaml:"use_ping_checks"`
		PingCheckWorkers       int           `yaml:"ping_check_workers"`
		PingUnprivileged       bool          `yaml:"ping_unprivileged"`
		PingCheckInterval      time.Duration `yaml:"ping_check_interval"`
		DataCollectionInterval time.Duration `yaml:"data_collection_interval"`
		CollectInterfaceData   bool          `yaml:"collect_interface_data"`
		CollectPeerData        bool          `yaml:"collect_peer_data"`
		CollectAuditData       bool          `yaml:"collect_audit_data"`
		ListeningAddress       string        `yaml:"listening_address"`
	} `yaml:"statistics"`

	Mail MailConfig `yaml:"mail"`

	Auth Auth `yaml:"auth"`

	Database DatabaseConfig `yaml:"database"`

	Web WebConfig `yaml:"web"`
}

func (c *Config) LogStartupValues() {
	logrus.Infof("Log Level: %s", c.Advanced.LogLevel)

	logrus.Debug("WireGuard Portal Features:")
	logrus.Debugf("  - EditableKeys: %t", c.Core.EditableKeys)
	logrus.Debugf("  - CreateDefaultPeerOnCreation: %t", c.Core.CreateDefaultPeerOnCreation)
	logrus.Debugf("  - ReEnablePeerAfterUserEnable: %t", c.Core.ReEnablePeerAfterUserEnable)
	logrus.Debugf("  - DeletePeerAfterUserDeleted: %t", c.Core.DeletePeerAfterUserDeleted)
	logrus.Debugf("  - SelfProvisioningAllowed: %t", c.Core.SelfProvisioningAllowed)
	logrus.Debugf("  - ImportExisting: %t", c.Core.ImportExisting)
	logrus.Debugf("  - RestoreState: %t", c.Core.RestoreState)
	logrus.Debugf("  - UseIpV6: %t", c.Advanced.UseIpV6)
	logrus.Debugf("  - CollectInterfaceData: %t", c.Statistics.CollectInterfaceData)
	logrus.Debugf("  - CollectPeerData: %t", c.Statistics.CollectPeerData)
	logrus.Debugf("  - CollectAuditData: %t", c.Statistics.CollectAuditData)

	logrus.Debug("WireGuard Portal Settings:")
	logrus.Debugf("  - ConfigStoragePath: %s", c.Advanced.ConfigStoragePath)
	logrus.Debugf("  - ExternalUrl: %s", c.Web.ExternalUrl)

	logrus.Debug("WireGuard Portal Authentication:")
	logrus.Debugf("  - OIDC Providers: %d", len(c.Auth.OpenIDConnect))
	logrus.Debugf("  - OAuth Providers: %d", len(c.Auth.OAuth))
	logrus.Debugf("  - Ldap Providers: %d", len(c.Auth.Ldap))
}

func defaultConfig() *Config {
	cfg := &Config{}

	cfg.Core.AdminUser = "admin@wgportal.local"
	cfg.Core.AdminPassword = "wgportal"
	cfg.Core.ImportExisting = true
	cfg.Core.RestoreState = true
	cfg.Core.CreateDefaultPeer = false
	cfg.Core.CreateDefaultPeerOnCreation = false
	cfg.Core.EditableKeys = true
	cfg.Core.SelfProvisioningAllowed = false
	cfg.Core.ReEnablePeerAfterUserEnable = true
	cfg.Core.DeletePeerAfterUserDeleted = false

	cfg.Database = DatabaseConfig{
		Type: "sqlite",
		DSN:  "data/sqlite.db",
	}

	cfg.Web = WebConfig{
		RequestLogging:    false,
		ExternalUrl:       "http://localhost:8888",
		ListeningAddress:  ":8888",
		SessionIdentifier: "wgPortalSession",
		SessionSecret:     "very_secret",
		CsrfSecret:        "extremely_secret",
		SiteTitle:         "WireGuard Portal",
		SiteCompanyName:   "WireGuard Portal",
	}

	cfg.Advanced.LogLevel = "info"
	cfg.Advanced.StartListenPort = 51820
	cfg.Advanced.StartCidrV4 = "10.11.12.0/24"
	cfg.Advanced.StartCidrV6 = "fdfd:d3ad:c0de:1234::0/64"
	cfg.Advanced.UseIpV6 = true
	cfg.Advanced.ExpiryCheckInterval = 15 * time.Minute
	cfg.Advanced.RulePrioOffset = 20000
	cfg.Advanced.RouteTableOffset = 20000
	cfg.Advanced.ApiAdminOnly = true

	cfg.Statistics.UsePingChecks = true
	cfg.Statistics.PingCheckWorkers = 10
	cfg.Statistics.PingUnprivileged = false
	cfg.Statistics.PingCheckInterval = 1 * time.Minute
	cfg.Statistics.DataCollectionInterval = 1 * time.Minute
	cfg.Statistics.CollectInterfaceData = true
	cfg.Statistics.CollectPeerData = true
	cfg.Statistics.CollectAuditData = true
	cfg.Statistics.ListeningAddress = ":8787"

	cfg.Mail = MailConfig{
		Host:           "127.0.0.1",
		Port:           25,
		Encryption:     MailEncryptionNone,
		CertValidation: false,
		Username:       "",
		Password:       "",
		AuthType:       MailAuthPlain,
		From:           "Wireguard Portal <noreply@wireguard.local>",
		LinkOnly:       false,
	}

	return cfg
}

func GetConfig() (*Config, error) {
	cfg := defaultConfig()

	// override config values from YAML file

	cfgFileName := "config/config.yml"
	if envCfgFileName := os.Getenv("WG_PORTAL_CONFIG"); envCfgFileName != "" {
		cfgFileName = envCfgFileName
	}

	if err := loadConfigFile(cfg, cfgFileName); err != nil {
		return nil, fmt.Errorf("failed to load config from yaml: %w", err)
	}

	return cfg, nil
}

func loadConfigFile(cfg any, filename string) error {
	data, err := envsubst.ReadFile(filename)
	if err != nil {
		if os.IsNotExist(err) {
			logrus.Warnf("Config file %s not found, using default values", filename)
			return nil
		}
		return fmt.Errorf("envsubst error: %v", err)
	}

	err = yaml.Unmarshal(data, cfg)
	if err != nil {
		return fmt.Errorf("yaml error: %v", err)
	}

	return nil
}
