package config

import (
	"fmt"
	"strconv"
	"strings"

	"warpbox/lib/security"
)

func (cfg *Config) ApplyOverrides(overrides map[string]string) error {
	if !cfg.AllowAdminSettingsOverride {
		return nil
	}
	for key, value := range overrides {
		if err := cfg.ApplyOverride(key, value); err != nil {
			return err
		}
	}
	return nil
}

func (cfg *Config) ApplyOverride(key string, value string) error {
	def, ok := Definition(key)
	if !ok {
		return fmt.Errorf("unknown setting %q", key)
	}
	if !def.Editable || def.HardLimit {
		return fmt.Errorf("setting %q cannot be changed from the admin UI", key)
	}

	value = strings.TrimSpace(value)
	if err := validateSecurityTextSetting(key, value); err != nil {
		return err
	}

	switch def.Type {
	case SettingTypeBool:
		parsed, err := parseBool(value)
		if err != nil {
			return fmt.Errorf("%s: %w", key, err)
		}
		cfg.assignBool(key, parsed, SourceDB)
	case SettingTypeInt64:
		parsed, err := parseInt64(value, def.Minimum)
		if err != nil {
			return fmt.Errorf("%s: %w", key, err)
		}
		cfg.assignInt64(key, parsed, SourceDB)
	case SettingTypeSizeGB:
		parsed, err := parseGigabytes(value, float64(def.Minimum))
		if err != nil {
			return fmt.Errorf("%s: %w", key, err)
		}
		cfg.assignInt64(key, parsed, SourceDB)
	case SettingTypeInt:
		parsed64, err := parseInt64(value, def.Minimum)
		if err != nil {
			return fmt.Errorf("%s: %w", key, err)
		}
		cfg.assignInt(key, int(parsed64), SourceDB)
	case SettingTypeText:
		cfg.assignText(key, value, SourceDB)
	default:
		return fmt.Errorf("setting %q is not runtime editable", key)
	}
	return nil
}

func validateSecurityTextSetting(key string, value string) error {
	switch key {
	case SettingSecurityIPWhitelist, SettingSecurityAdminIPWhitelist:
		if _, err := security.ParseIPMatchers(value, true); err != nil {
			return fmt.Errorf("%s: %w", key, err)
		}
	case SettingTrustedProxyCIDRs:
		if _, err := security.ParseCIDRList(value); err != nil {
			return fmt.Errorf("%s: %w", key, err)
		}
	}
	return nil
}

func (cfg *Config) assignBool(key string, value bool, source Source) {
	switch key {
	case SettingGuestUploadsEnabled:
		cfg.GuestUploadsEnabled = value
	case SettingAPIEnabled:
		cfg.APIEnabled = value
	case SettingZipDownloadsEnabled:
		cfg.ZipDownloadsEnabled = value
	case SettingOneTimeDownloadsEnabled:
		cfg.OneTimeDownloadsEnabled = value
	case SettingRenewOnAccessEnabled:
		cfg.RenewOnAccessEnabled = value
	case SettingRenewOnDownloadEnabled:
		cfg.RenewOnDownloadEnabled = value
	}
	cfg.setValue(key, formatBool(value), source)
}

func (cfg *Config) assignInt64(key string, value int64, source Source) {
	switch key {
	case SettingDefaultGuestExpirySecs:
		cfg.DefaultGuestExpirySeconds = value
	case SettingMaxGuestExpirySecs:
		cfg.MaxGuestExpirySeconds = value
	case SettingOneTimeDownloadExpirySecs:
		cfg.OneTimeDownloadExpirySeconds = value
	case SettingGlobalMaxFileSizeBytes:
		cfg.GlobalMaxFileSizeBytes = value
	case SettingGlobalMaxBoxSizeBytes:
		cfg.GlobalMaxBoxSizeBytes = value
	case SettingDefaultUserMaxFileBytes:
		cfg.DefaultUserMaxFileSizeBytes = value
	case SettingDefaultUserMaxBoxBytes:
		cfg.DefaultUserMaxBoxSizeBytes = value
	case SettingSessionTTLSeconds:
		cfg.SessionTTLSeconds = value
	case SettingActivityRetentionSeconds:
		cfg.ActivityRetentionSeconds = value
	case SettingSecurityLoginWindowSecs:
		cfg.SecurityLoginWindowSeconds = value
	case SettingSecurityBanSeconds:
		cfg.SecurityBanSeconds = value
	case SettingSecurityScanWindowSecs:
		cfg.SecurityScanWindowSeconds = value
	case SettingSecurityUploadWindowSecs:
		cfg.SecurityUploadWindowSeconds = value
	case SettingSecurityUploadMaxGB:
		cfg.SecurityUploadMaxBytes = value
	}
	if key == SettingGlobalMaxFileSizeBytes || key == SettingGlobalMaxBoxSizeBytes || key == SettingDefaultUserMaxFileBytes || key == SettingDefaultUserMaxBoxBytes || key == SettingSecurityUploadMaxGB {
		cfg.setValue(key, formatGigabytesFromBytes(value), source)
		return
	}
	cfg.setValue(key, strconv.FormatInt(value, 10), source)
}

func (cfg *Config) assignInt(key string, value int, source Source) {
	switch key {
	case SettingBoxPollIntervalMS:
		cfg.BoxPollIntervalMS = value
	case SettingThumbnailBatchSize:
		cfg.ThumbnailBatchSize = value
	case SettingThumbnailIntervalSeconds:
		cfg.ThumbnailIntervalSeconds = value
	case SettingSecurityLoginMaxAttempts:
		cfg.SecurityLoginMaxAttempts = value
	case SettingSecurityScanMaxAttempts:
		cfg.SecurityScanMaxAttempts = value
	case SettingSecurityUploadMaxRequests:
		cfg.SecurityUploadMaxRequests = value
	}
	cfg.setValue(key, strconv.Itoa(value), source)
}

func (cfg *Config) assignText(key string, value string, source Source) {
	switch key {
	case SettingSecurityIPWhitelist:
		cfg.SecurityIPWhitelist = value
	case SettingSecurityAdminIPWhitelist:
		cfg.SecurityAdminIPWhitelist = value
	case SettingTrustedProxyCIDRs:
		cfg.TrustedProxyCIDRs = value
	}
	cfg.setValue(key, value, source)
}

func (cfg *Config) setValue(key string, value string, source Source) {
	if key == "" {
		return
	}
	cfg.values[key] = value
	cfg.sources[key] = source
}

func (cfg *Config) sourceFor(key string) Source {
	source, ok := cfg.sources[key]
	if !ok {
		return SourceDefault
	}
	return source
}

func (cfg *Config) DefaultValue(key string) string {
	if cfg.defaults == nil {
		return ""
	}
	return cfg.defaults[key]
}
