package config

import (
	"os"
	"strings"

	"github.com/tidbcloud/pkgv2/auth/auth0"
	"github.com/tidbcloud/tiinf/sdk/auth"
)

var MyNamespace = os.Getenv("MY_NAMESPACE")

type (
	Auth0ConnectionType string
	ContextKey          string
)

const (
	Auth0DatabaseConnection     Auth0ConnectionType = "custom-email"
	Auth0SocialConnectionGoogle Auth0ConnectionType = "google-oauth2"
	Auth0SocialConnectionGithub Auth0ConnectionType = "github"

	// CtxIdentityKey public key for open api and userid for console/accesstoken
	CtxIdentityKey           ContextKey = "ctxIdentityKey"
	CtxGRPCGatewayTokenKey   ContextKey = auth.GrpcGatewayMetadataToken
	CtxGRPCGatewayTokenValue ContextKey = auth.GrpcGatewayMetadataVal
	CtxOpenApiRoles          ContextKey = "ctxOpenApiRoles"

	CtxWorkerNameKey ContextKey = "workerName"
	CtxEventIDKey    ContextKey = "eventID"

	CtxIdentityTypeKey ContextKey = "identityType"
	CtxIdentityValKey  ContextKey = "identityVal"
)

const (
	ApiKeyAuthType      = "apikey"
	AccessTokenAuthType = "accessToken"
	MailAuthType        = auth0.GmailAuthType
)

func (c Auth0ConnectionType) String() string {
	return string(c)
}

// Deployment stage
const (
	StageLocal   = "local"
	StageDev     = "dev"
	StageStaging = "staging"
	StageProd    = "prod"
)

func Stage() string {
	if globalConf == nil {
		return ""
	}
	return globalConf.ServerConfig.Stage
}

func IsTestStage() bool {
	if globalConf == nil {
		return false
	}

	stage := globalConf.ServerConfig.Stage
	return stage == StageLocal || stage == StageDev
}

func IsLocalStage() bool {
	if globalConf == nil {
		return false
	}

	return globalConf.ServerConfig.Stage == StageLocal
}

type WorkMode string

// Work mode
const (
	WorkModeHttpServer WorkMode = "http"
	WorkModeGrpcServer WorkMode = "grpc"
	WorkModeOnetimeJob WorkMode = "job"
	WorkModeWorker     WorkMode = "worker"
	WorkModeBRWorker   WorkMode = "br-worker"
	WorkModeWorkflow   WorkMode = "workflow"
)

func (wm WorkMode) String() string {
	return string(wm)
}

var workMode WorkMode

func SetWorkMode(wm WorkMode) {
	workMode = wm
}

func GetWorkMode() WorkMode {
	return workMode
}

func IsWorkerMode() bool {
	return workMode == WorkModeWorker
}

func IsBRWorkerMode() bool {
	return workMode == WorkModeBRWorker
}

func IsWorkflowMode() bool {
	return workMode == WorkModeWorkflow
}

// https://github.com/tidbcloud/tidb-management-service/blob/e83bc776f15005356827fff9dcd0e35de0a5625b/api/spec/tidb_cloud_open_api/common/v1beta1/region.proto#L47-L62
const (
	ProviderAWS      = "aws"
	ProviderGCP      = "gcp"
	ProviderAzure    = "azure"
	ProviderAliCloud = "alicloud"
)

// HasS3CompatibleStashing reports whether local import/export stashing
// uses an S3 (or S3-compatible OSS) client for the given provider.
// GCP stashing is skipped until a GCS client is wired; adding GCP to
// RegionalConfig must not panic at process start.
func HasS3CompatibleStashing(provider string) bool {
	return provider == ProviderAWS || provider == ProviderAliCloud
}

func GetO11yUrl(provider, region, kubernetes string) string {
	return GetKubernetesConfig(provider, region, kubernetes).O11yUrl
}

func GetClusterProxyUrl(provider, region, kubernetes string) string {
	return GetKubernetesConfig(provider, region, kubernetes).ClusterProxyUrl
}

func GetKubernetesConfig(provider, region, kubernetes string) K8sConfig {
	for _, cfg := range Global().WorkerConfig.RegionalConfig {
		if cfg.Provider == provider && cfg.Region == region {
			for _, k8s := range cfg.K8sConfig {
				if k8s.Kubernetes == kubernetes {
					return k8s
				}
			}
		}
	}
	return K8sConfig{}
}

// ResolveNewArchBaseImage returns the TiCDC base image to use for a
// newarch shared cluster scheduled into the given (provider, region).
// Resolution order, highest priority first:
//
//  1. cdc.newArch.regionConfig[] — per-region override. Lets a
//     specific region pull from a region-local registry or canary a
//     different image without coupling CDC rollout config to worker
//     region config.
//  2. cdc.newArch.baseImagePerProvider[provider] — per-provider default.
//     Each cloud has its own registry layout (ECR / GCR / ACR), so a
//     single string can't cover all of them. Lookup is case-insensitive
//     on the provider key.
//  3. cdc.newArch.baseImage — global fallback.
//
// Returns the empty string when nothing is configured; the caller is
// expected to surface a configuration error in that case (see
// validateCreateChangefeedDefaults).
func ResolveNewArchBaseImage(provider, region string) string {
	newArch := Global().CDCConfig.NewArch
	for _, cfg := range newArch.RegionConfig {
		if newArchRegionalConfigMatches(cfg, provider, region) && cfg.BaseImage != "" {
			return cfg.BaseImage
		}
	}
	if newArch.BaseImagePerProvider != nil {
		if img, ok := newArch.BaseImagePerProvider[provider]; ok && img != "" {
			return img
		}
		if img, ok := newArch.BaseImagePerProvider[strings.ToLower(provider)]; ok && img != "" {
			return img
		}
	}
	return newArch.BaseImage
}

// ResolveNewArchVersion returns the configured TiCDC image tag for a
// newarch shared cluster. Existing shards keep the version stored in
// ticdc_clusters; this resolver is only the default for brand-new
// shards or deleted-shard bookkeeping.
func ResolveNewArchVersion(provider, region string) string {
	newArch := Global().CDCConfig.NewArch
	for _, cfg := range newArch.RegionConfig {
		if newArchRegionalConfigMatches(cfg, provider, region) && cfg.Version != "" {
			return cfg.Version
		}
	}
	return newArch.Version
}

func newArchRegionalConfigMatches(cfg CDCNewArchRegionalConfig, provider, region string) bool {
	return strings.EqualFold(cfg.Provider, provider) && cfg.Region == region
}
