package config

import (
	"log"
	"os"
	"strings"

	"github.com/go-playground/validator/v10"
	"github.com/spf13/viper"
)

type ServerConfig struct {
	Port                 string `validate:"required"`
	RootUrl              string `validate:"required,url"`
	SqliteDatabasePath   string `validate:"required"`
	KeyValueDatabasePath string `validate:"required"`
	SessionSecret        string `validate:"required"`

	Jwt    ServerJwt    `validate:"required"`
	OAuth2 ServerOAuth2 `validate:"required"`

	Temporal ServerTemporal `validate:"required"`
}

type ServerJwt struct {
	PrivateKey string `validate:"required"`
	PublicKey  string `validate:"required"`
}

type ServerOAuth2 struct {
	ClientID            string   `validate:"required"`
	ClientSecret        string   `validate:"required"`
	Scopes              []string `validate:"required"`
	EndpointTokenURL    string   `validate:"required"`
	EndpointAuthURL     string   `validate:"required"`
	EndpointUserInfoURL string   `validate:"required"`
	EndpointLogoutURL   string   // Optional as not all SSO support this.
}

type ServerTemporal struct {
	UIHost     string `validate:"required"`
	ServerHost string `validate:"required"`
}

func NewServerConfig() *ServerConfig {
	v := newViper()

	// Set defaults
	v.SetDefault("port", GetEnvOrDefault("PORT", "8000"))
	v.SetDefault("rooturl", GetEnvOrDefault("ROOT_URL", "http://localhost:8000"))
	v.SetDefault("sqlitedatabasepath", GetEnvOrDefault("SQLITE_DATABASE_PATH", "store/zdravko.db"))
	v.SetDefault("keyvaluedatabasepath", GetEnvOrDefault("KEYVALUE_DATABASE_PATH", "store/zdravko_kv.db"))
	v.SetDefault("sessionsecret", os.Getenv("SESSION_SECRET"))
	v.SetDefault("temporal.uihost", GetEnvOrDefault("TEMPORAL_UI_HOST", "127.0.0.1:8223"))
	v.SetDefault("temporal.serverhost", GetEnvOrDefault("TEMPORAL_SERVER_HOST", "127.0.0.1:7233"))
	v.SetDefault("jwt.privatekey", os.Getenv("JWT_PRIVATE_KEY"))
	v.SetDefault("jwt.publickey", os.Getenv("JWT_PUBLIC_KEY"))
	v.SetDefault("oauth2.clientid", os.Getenv("OAUTH2_CLIENT_ID"))
	v.SetDefault("oauth2.clientsecret", os.Getenv("OAUTH2_CLIENT_SECRET"))
	v.SetDefault("oauth2.scopes", GetEnvOrDefault("OAUTH2_ENDPOINT_SCOPES", "openid profile email"))
	v.SetDefault("oauth2.endpointtokenurl", os.Getenv("OAUTH2_ENDPOINT_TOKEN_URL"))
	v.SetDefault("oauth2.endpointauthurl", os.Getenv("OAUTH2_ENDPOINT_AUTH_URL"))
	v.SetDefault("oauth2.endpointuserinfourl", os.Getenv("OAUTH2_ENDPOINT_USER_INFO_URL"))
	v.SetDefault("oauth2.endpointlogouturl", GetEnvOrDefault("OAUTH2_ENDPOINT_LOGOUT_URL", ""))

	err := v.ReadInConfig()
	if err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			// ignore
		} else {
			log.Fatalf("Error reading config file, %s", err)
		}
	}
	log.Println("Config file used: ", v.ConfigFileUsed())

	config := &ServerConfig{}
	err = v.Unmarshal(config)
	if err != nil {
		log.Fatalf("Error unmarshalling config, %s", err)
	}

	// OAuth2 scopes are space separated
	config.OAuth2.Scopes = strings.Split(v.GetString("oauth2.scopes"), " ")

	// Validate config
	validate := validator.New(validator.WithRequiredStructEnabled())
	err = validate.Struct(config)
	if err != nil {
		log.Fatalf("Error validating config, %s", err)
	}

	return config
}
