package service

import (
	"context"
	"fmt"
	"strings"
)

// OAuth provider 配置键;client secret 以 AES-GCM 密文落库。
const (
	settingOauthOidcIssuer         = "oauth_oidc_issuer"
	settingOauthOidcClientID       = "oauth_oidc_client_id"
	settingOauthOidcClientSecret   = "oauth_oidc_client_secret"
	settingOauthOidcDisplayName    = "oauth_oidc_display_name"
	settingOauthOidcDisabled       = "oauth_oidc_disabled"
	settingOauthGithubClientID     = "oauth_github_client_id"
	settingOauthGithubClientSecret = "oauth_github_client_secret"
	settingOauthGithubDisplayName  = "oauth_github_display_name"
	settingOauthGithubDisabled     = "oauth_github_disabled"
)

// OAuthProvidersView 是 OAuth provider 配置视图;绝不返回 secret 明文。
type OAuthProvidersView struct {
	OidcIssuer        string `json:"oidcIssuer"`
	OidcClientID      string `json:"oidcClientId"`
	OidcSecretSet     bool   `json:"oidcSecretSet"`
	OidcDisplayName   string `json:"oidcDisplayName"`
	OidcDisabled      bool   `json:"oidcDisabled"`
	GithubClientID    string `json:"githubClientId"`
	GithubSecretSet   bool   `json:"githubSecretSet"`
	GithubDisplayName string `json:"githubDisplayName"`
	GithubDisabled    bool   `json:"githubDisabled"`
}

// UpdateOAuthInput 是保存 provider 配置的输入;
// secret 为 nil 沿用已存值,非 nil 覆盖(空串清除)。
type UpdateOAuthInput struct {
	OidcIssuer         string  `json:"oidcIssuer"`
	OidcClientID       string  `json:"oidcClientId"`
	OidcClientSecret   *string `json:"oidcClientSecret"`
	OidcDisplayName    string  `json:"oidcDisplayName"`
	OidcDisabled       bool    `json:"oidcDisabled"`
	GithubClientID     string  `json:"githubClientId"`
	GithubClientSecret *string `json:"githubClientSecret"`
	GithubDisplayName  string  `json:"githubDisplayName"`
	GithubDisabled     bool    `json:"githubDisabled"`
}

// OAuthView 返回脱敏后的 provider 配置。
func (s *SettingService) OAuthView(ctx context.Context) (OAuthProvidersView, error) {
	var view OAuthProvidersView
	vals, err := s.getMany(ctx,
		settingOauthOidcIssuer, settingOauthOidcClientID, settingOauthOidcClientSecret,
		settingOauthOidcDisplayName, settingOauthOidcDisabled,
		settingOauthGithubClientID, settingOauthGithubClientSecret,
		settingOauthGithubDisplayName, settingOauthGithubDisabled)
	if err != nil {
		return view, err
	}
	view.OidcIssuer = vals[settingOauthOidcIssuer]
	view.OidcClientID = vals[settingOauthOidcClientID]
	view.OidcSecretSet = vals[settingOauthOidcClientSecret] != ""
	view.OidcDisplayName = vals[settingOauthOidcDisplayName]
	view.OidcDisabled = vals[settingOauthOidcDisabled] == "1"
	view.GithubClientID = vals[settingOauthGithubClientID]
	view.GithubSecretSet = vals[settingOauthGithubClientSecret] != ""
	view.GithubDisplayName = vals[settingOauthGithubDisplayName]
	view.GithubDisabled = vals[settingOauthGithubDisabled] == "1"
	return view, nil
}

// boolFlag 把开关序列化为 settings 存储值。
func boolFlag(on bool) string {
	if on {
		return "1"
	}
	return ""
}

// UpdateOAuth 保存 provider 配置;issuer 规范化去尾斜杠,secret 加密落库。
func (s *SettingService) UpdateOAuth(ctx context.Context, in UpdateOAuthInput) error {
	plain := map[string]string{
		settingOauthOidcIssuer:        strings.TrimRight(strings.TrimSpace(in.OidcIssuer), "/"),
		settingOauthOidcClientID:      strings.TrimSpace(in.OidcClientID),
		settingOauthOidcDisplayName:   strings.TrimSpace(in.OidcDisplayName),
		settingOauthOidcDisabled:      boolFlag(in.OidcDisabled),
		settingOauthGithubClientID:    strings.TrimSpace(in.GithubClientID),
		settingOauthGithubDisplayName: strings.TrimSpace(in.GithubDisplayName),
		settingOauthGithubDisabled:    boolFlag(in.GithubDisabled),
	}
	for key, value := range plain {
		if err := s.set(ctx, key, value); err != nil {
			return err
		}
	}
	if err := s.saveOAuthSecret(ctx, settingOauthOidcClientSecret, in.OidcClientSecret); err != nil {
		return err
	}
	return s.saveOAuthSecret(ctx, settingOauthGithubClientSecret, in.GithubClientSecret)
}

// saveOAuthSecret 加密保存 secret;nil 沿用,空串清除。
func (s *SettingService) saveOAuthSecret(ctx context.Context, key string, secret *string) error {
	if secret == nil {
		return nil
	}
	if *secret == "" {
		return s.set(ctx, key, "")
	}
	enc, err := s.cipher.EncryptString(*secret)
	if err != nil {
		return fmt.Errorf("encrypt oauth secret: %w", err)
	}
	return s.set(ctx, key, enc)
}

// oauthClient 返回 provider 的 clientID/明文 secret/issuer(仅 oidc);未配置时 clientID 为空。
func (s *SettingService) oauthClient(ctx context.Context, provider string) (clientID, secret, issuer string, err error) {
	idKey, secKey := settingOauthGithubClientID, settingOauthGithubClientSecret
	if provider == "oidc" {
		idKey, secKey = settingOauthOidcClientID, settingOauthOidcClientSecret
		if issuer, err = s.get(ctx, settingOauthOidcIssuer); err != nil {
			return
		}
	}
	if clientID, err = s.get(ctx, idKey); err != nil {
		return
	}
	enc, err := s.get(ctx, secKey)
	if err != nil || enc == "" {
		return
	}
	if secret, err = s.cipher.DecryptString(enc); err != nil {
		err = fmt.Errorf("decrypt oauth secret: %w", err)
	}
	return
}

// oauthProviderMeta 返回 provider 的展示名(空则给默认名)与禁用态。
func (s *SettingService) oauthProviderMeta(ctx context.Context, provider string) (display string, disabled bool, err error) {
	nameKey, offKey, def := settingOauthGithubDisplayName, settingOauthGithubDisabled, "GitHub"
	if provider == "oidc" {
		nameKey, offKey, def = settingOauthOidcDisplayName, settingOauthOidcDisabled, "OIDC 单点登录"
	}
	if display, err = s.get(ctx, nameKey); err != nil {
		return
	}
	if display == "" {
		display = def
	}
	off, err := s.get(ctx, offKey)
	disabled = off == "1"
	return
}
