package revaconfig

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"

	"github.com/qsfera/server/pkg/config/defaults"
	"github.com/qsfera/server/pkg/generators"
	"github.com/qsfera/server/pkg/log"
	"github.com/qsfera/server/services/sharing/pkg/config"
)

// SharingConfigFromStruct will adapt a КуСфера config struct into a reva mapstructure to start a reva service.
func SharingConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string]any, error) {
	passwordPolicyCfg, err := passwordPolicyConfig(cfg)
	if err != nil {
		logger.Err(err).Send()
		return nil, err
	}
	rcfg := map[string]any{
		"shared": map[string]any{
			"jwt_secret":                cfg.TokenManager.JWTSecret,
			"gatewaysvc":                cfg.Reva.Address,
			"skip_user_groups_in_token": cfg.SkipUserGroupsInToken,
			"grpc_client_options":       cfg.Reva.GetGRPCClientConfig(),
			"multi_tenant_enabled":      cfg.Commons.MultiTenantEnabled,
		},
		"grpc": map[string]any{
			"network": cfg.GRPC.Protocol,
			"address": cfg.GRPC.Addr,
			"tls_settings": map[string]any{
				"enabled":     cfg.GRPC.TLS.Enabled,
				"certificate": cfg.GRPC.TLS.Cert,
				"key":         cfg.GRPC.TLS.Key,
			},
			// TODO build services dynamically
			"services": map[string]any{
				"usershareprovider": map[string]any{
					"driver": cfg.UserSharingDriver,
					"drivers": map[string]any{
						"json": map[string]any{
							"file":         cfg.UserSharingDrivers.JSON.File,
							"gateway_addr": cfg.Reva.Address,
						},
						"sql": map[string]any{ // cernbox sql
							"db_username":                   cfg.UserSharingDrivers.SQL.DBUsername,
							"db_password":                   cfg.UserSharingDrivers.SQL.DBPassword,
							"db_host":                       cfg.UserSharingDrivers.SQL.DBHost,
							"db_port":                       cfg.UserSharingDrivers.SQL.DBPort,
							"db_name":                       cfg.UserSharingDrivers.SQL.DBName,
							"password_hash_cost":            cfg.UserSharingDrivers.SQL.PasswordHashCost,
							"enable_expired_shares_cleanup": cfg.UserSharingDrivers.SQL.EnableExpiredSharesCleanup,
							"janitor_run_interval":          cfg.UserSharingDrivers.SQL.JanitorRunInterval,
						},
						"owncloudsql": map[string]any{
							"gateway_addr":     cfg.Reva.Address,
							"storage_mount_id": cfg.UserSharingDrivers.OwnCloudSQL.UserStorageMountID,
							"db_username":      cfg.UserSharingDrivers.OwnCloudSQL.DBUsername,
							"db_password":      cfg.UserSharingDrivers.OwnCloudSQL.DBPassword,
							"db_host":          cfg.UserSharingDrivers.OwnCloudSQL.DBHost,
							"db_port":          cfg.UserSharingDrivers.OwnCloudSQL.DBPort,
							"db_name":          cfg.UserSharingDrivers.OwnCloudSQL.DBName,
						},
						"cs3": map[string]any{
							"gateway_addr":        cfg.UserSharingDrivers.CS3.ProviderAddr,
							"provider_addr":       cfg.UserSharingDrivers.CS3.ProviderAddr,
							"service_user_id":     cfg.UserSharingDrivers.CS3.SystemUserID,
							"service_user_idp":    cfg.UserSharingDrivers.CS3.SystemUserIDP,
							"machine_auth_apikey": cfg.UserSharingDrivers.CS3.SystemUserAPIKey,
						},
						"jsoncs3": map[string]any{
							"gateway_addr":           cfg.Reva.Address,
							"provider_addr":          cfg.UserSharingDrivers.JSONCS3.ProviderAddr,
							"system_user_id":         cfg.UserSharingDrivers.JSONCS3.SystemUserID,
							"system_user_idp":        cfg.UserSharingDrivers.JSONCS3.SystemUserIDP,
							"machine_auth_apikey":    cfg.UserSharingDrivers.JSONCS3.SystemUserAPIKey,
							"ttl":                    cfg.UserSharingDrivers.JSONCS3.CacheTTL,
							"max_concurrency":        cfg.UserSharingDrivers.JSONCS3.MaxConcurrency,
							"service_account_id":     cfg.ServiceAccount.ServiceAccountID,
							"service_account_secret": cfg.ServiceAccount.ServiceAccountSecret,
							"events": map[string]any{
								"natsaddress":          cfg.Events.Addr,
								"natsclusterid":        cfg.Events.ClusterID,
								"tlsinsecure":          cfg.Events.TLSInsecure,
								"tlsrootcacertificate": cfg.Events.TLSRootCaCertPath,
								"authusername":         cfg.Events.AuthUsername,
								"authpassword":         cfg.Events.AuthPassword,
							},
						},
					},
				},
				"publicshareprovider": map[string]any{
					"gateway_addr":                       cfg.Reva.Address,
					"writeable_share_must_have_password": cfg.WriteableShareMustHavePassword,
					"public_share_must_have_password":    cfg.PublicShareMustHavePassword,
					"password_policy":                    passwordPolicyCfg,
					"driver":                             cfg.PublicSharingDriver,
					"drivers": map[string]any{
						"json": map[string]any{
							"file":         cfg.PublicSharingDrivers.JSON.File,
							"gateway_addr": cfg.Reva.Address,
						},
						"sql": map[string]any{
							"db_username":                   cfg.PublicSharingDrivers.SQL.DBUsername,
							"db_password":                   cfg.PublicSharingDrivers.SQL.DBPassword,
							"db_host":                       cfg.PublicSharingDrivers.SQL.DBHost,
							"db_port":                       cfg.PublicSharingDrivers.SQL.DBPort,
							"db_name":                       cfg.PublicSharingDrivers.SQL.DBName,
							"password_hash_cost":            cfg.PublicSharingDrivers.SQL.PasswordHashCost,
							"enable_expired_shares_cleanup": cfg.PublicSharingDrivers.SQL.EnableExpiredSharesCleanup,
							"janitor_run_interval":          cfg.PublicSharingDrivers.SQL.JanitorRunInterval,
						},
						"cs3": map[string]any{
							"gateway_addr":        cfg.PublicSharingDrivers.CS3.ProviderAddr,
							"provider_addr":       cfg.PublicSharingDrivers.CS3.ProviderAddr,
							"service_user_id":     cfg.PublicSharingDrivers.CS3.SystemUserID,
							"service_user_idp":    cfg.PublicSharingDrivers.CS3.SystemUserIDP,
							"machine_auth_apikey": cfg.PublicSharingDrivers.CS3.SystemUserAPIKey,
						},
						"jsoncs3": map[string]any{
							"gateway_addr":                  cfg.Reva.Address,
							"provider_addr":                 cfg.PublicSharingDrivers.JSONCS3.ProviderAddr,
							"service_user_id":               cfg.PublicSharingDrivers.JSONCS3.SystemUserID,
							"service_user_idp":              cfg.PublicSharingDrivers.JSONCS3.SystemUserIDP,
							"machine_auth_apikey":           cfg.PublicSharingDrivers.JSONCS3.SystemUserAPIKey,
							"enable_expired_shares_cleanup": cfg.EnableExpiredSharesCleanup,
						},
					},
				},
			},
			"interceptors": map[string]any{
				"eventsmiddleware": map[string]any{
					"group":            "sharing",
					"type":             "nats",
					"address":          cfg.Events.Addr,
					"clusterID":        cfg.Events.ClusterID,
					"tls-insecure":     cfg.Events.TLSInsecure,
					"tls-root-ca-cert": cfg.Events.TLSRootCaCertPath,
					"enable-tls":       cfg.Events.EnableTLS,
					"name":             generators.GenerateConnectionName(cfg.Service.Name, generators.NTypeBus),
					"username":         cfg.Events.AuthUsername,
					"password":         cfg.Events.AuthPassword,
				},
				"prometheus": map[string]any{
					"namespace": "qsfera",
					"subsystem": "sharing",
				},
			},
		},
	}
	return rcfg, nil
}

func readMultilineFile(path string) (map[string]struct{}, error) {
	if !fileExists(path) {
		path = filepath.Join(defaults.BaseConfigPath(), path)
	}
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	data := make(map[string]struct{})
	for scanner.Scan() {
		line := scanner.Text()
		if line != "" {
			data[line] = struct{}{}
		}
	}
	return data, err
}

func fileExists(path string) bool {
	info, err := os.Stat(path)
	if err != nil {
		return false
	}
	return !info.IsDir()
}

func passwordPolicyConfig(cfg *config.Config) (map[string]any, error) {
	_maxCharacters := 72
	if cfg.PasswordPolicy.Disabled {
		return map[string]any{
			"max_characters":        _maxCharacters,
			"banned_passwords_list": nil,
		}, nil
	}
	var bannedPasswordsList map[string]struct{}
	var err error
	if cfg.PasswordPolicy.BannedPasswordsList != "" {
		bannedPasswordsList, err = readMultilineFile(cfg.PasswordPolicy.BannedPasswordsList)
		if err != nil {
			return nil, fmt.Errorf("failed to load the banned passwords from a file %s: %w", cfg.PasswordPolicy.BannedPasswordsList, err)
		}
	}
	return map[string]any{
		"max_characters":           _maxCharacters,
		"min_digits":               cfg.PasswordPolicy.MinDigits,
		"min_characters":           cfg.PasswordPolicy.MinCharacters,
		"min_lowercase_characters": cfg.PasswordPolicy.MinLowerCaseCharacters,
		"min_uppercase_characters": cfg.PasswordPolicy.MinUpperCaseCharacters,
		"min_special_characters":   cfg.PasswordPolicy.MinSpecialCharacters,
		"banned_passwords_list":    bannedPasswordsList,
	}, nil
}
