package auth

import (
	"errors"
	"net/netip"
	"net/url"
	"strings"
)

const desktopOIDCRedirectPath = "/oidc/callback"

var errPingCAPAuthClientNotConfigured = errors.New("pingcap auth client is not configured")

// PingCAPAuthConfig holds PingCAP User Center OIDC settings.
type PingCAPAuthConfig struct {
	Enabled             bool
	Issuer              string
	ClientID            string
	ClientSecret        string
	WebClientID         string
	WebClientSecret     string
	DesktopClientID     string
	DesktopClientSecret string
	RedirectURI         string
	Scopes              []string
}

func pingCAPAuthConfigValue(provider func() PingCAPAuthConfig) PingCAPAuthConfig {
	if provider == nil {
		return PingCAPAuthConfig{}
	}
	return provider()
}

type pingCAPOIDCClient struct {
	clientID     string
	clientSecret string
}

func (config PingCAPAuthConfig) oidcClientForRedirectURI(redirectURI string) pingCAPOIDCClient {
	if isNativeLoopbackRedirectURI(redirectURI) {
		return pingCAPOIDCClient{
			clientID:     strings.TrimSpace(config.DesktopClientID),
			clientSecret: strings.TrimSpace(config.DesktopClientSecret),
		}
	}
	return config.webOIDCClient()
}

func (config PingCAPAuthConfig) webOIDCClient() pingCAPOIDCClient {
	if strings.TrimSpace(config.WebClientID) != "" {
		return pingCAPOIDCClient{
			clientID:     strings.TrimSpace(config.WebClientID),
			clientSecret: strings.TrimSpace(config.WebClientSecret),
		}
	}
	return pingCAPOIDCClient{
		clientID:     strings.TrimSpace(config.ClientID),
		clientSecret: strings.TrimSpace(config.ClientSecret),
	}
}

func isNativeLoopbackRedirectURI(raw string) bool {
	parsed, err := url.Parse(strings.TrimSpace(raw))
	if err != nil {
		return false
	}
	if parsed.Scheme != "http" || parsed.Path != desktopOIDCRedirectPath {
		return false
	}
	if parsed.Port() == "" || parsed.RawQuery != "" || parsed.Fragment != "" || parsed.User != nil {
		return false
	}
	addr, err := netip.ParseAddr(parsed.Hostname())
	return err == nil && addr.IsLoopback()
}
