/*
 * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package config

import (
	"context"
	"os"
	"strings"

	"github.com/pkg/errors"
	corev1 "k8s.io/api/core/v1"
	k8serrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	"github.com/dynemo-ai/dynemo/deploy/compoundai/operator/pkg/compoundai/consts"
)

func GetYataiSystemNamespaceFromEnv() string {
	return getEnv(consts.EnvYataiSystemNamespace, consts.DefaultKubeNamespaceYataiSystem)
}

func GetYataiImageBuilderNamespace(ctx context.Context, secretGetter func(ctx context.Context, namespace, name string) (*corev1.Secret, error)) (namespace string, err error) {
	namespace = os.Getenv(consts.EnvYataiImageBuilderNamespace)
	if namespace != "" {
		return
	}

	yataiSystemNamespace := GetYataiSystemNamespaceFromEnv()
	yataiImageBuilderSharedEnvSecretName := consts.KubeSecretNameYataiImageBuilderSharedEnv

	secret, err := secretGetter(ctx, yataiSystemNamespace, yataiImageBuilderSharedEnvSecretName)
	if err != nil {
		if k8serrors.IsNotFound(err) {
			err = errors.Wrapf(err, "secret %s not found in namespace %s", yataiImageBuilderSharedEnvSecretName, yataiSystemNamespace)
		}
		return
	}

	namespace = string(secret.Data[consts.EnvYataiImageBuilderNamespace])
	if namespace == "" {
		namespace = consts.DefaultKubeNamespaceYataiImageBuilderComponent
	}

	return
}

func GetYataiDeploymentNamespace(ctx context.Context, secretGetter func(ctx context.Context, namespace, name string) (*corev1.Secret, error)) (namespace string, err error) {
	namespace = os.Getenv(consts.EnvYataiDeploymentNamespace)
	if namespace != "" {
		return
	}

	yataiSystemNamespace := GetYataiSystemNamespaceFromEnv()
	yataiDeploymentSharedEnvSecretName := consts.KubeSecretNameYataiDeploymentSharedEnv

	secret, err := secretGetter(ctx, yataiSystemNamespace, yataiDeploymentSharedEnvSecretName)
	if err != nil {
		if k8serrors.IsNotFound(err) {
			err = errors.Wrapf(err, "secret %s not found in namespace %s", yataiDeploymentSharedEnvSecretName, yataiSystemNamespace)
		}
		return
	}

	namespace = string(secret.Data[consts.EnvYataiDeploymentNamespace])
	if namespace == "" {
		namespace = consts.DefaultKubeNamespaceYataiDeploymentComponent
	}

	return
}

func GetImageBuildersNamespace(ctx context.Context, cliset *kubernetes.Clientset) (namespace string, err error) {
	namespace = os.Getenv(consts.EnvImageBuildersNamespace)
	if namespace != "" {
		return
	}

	yataiSystemNamespace := GetYataiSystemNamespaceFromEnv()
	yataiImageBuilderSharedEnvSecretName := consts.KubeSecretNameYataiImageBuilderSharedEnv

	secret, err := cliset.CoreV1().Secrets(yataiSystemNamespace).Get(ctx, yataiImageBuilderSharedEnvSecretName, metav1.GetOptions{})
	if err != nil {
		if k8serrors.IsNotFound(err) {
			err = errors.Wrapf(err, "secret %s not found in namespace %s", yataiImageBuilderSharedEnvSecretName, yataiSystemNamespace)
		}
		return
	}

	namespace = string(secret.Data[consts.EnvImageBuildersNamespace])
	if namespace == "" {
		namespace = consts.DefaultKubeNamespaceImageBuilders
	}

	return
}

func GetBentoDeploymentNamespaces(ctx context.Context, secretGetter func(ctx context.Context, namespace, name string) (*corev1.Secret, error)) (namespaces []string, err error) {
	namespaces_ := os.Getenv(consts.EnvBentoDeploymentNamespaces)
	if namespaces_ != "" {
		namespaces = strings.Split(namespaces_, ",")
		return
	}

	yataiSystemNamespace := GetYataiSystemNamespaceFromEnv()
	yataiDeploymentSharedEnvSecretName := consts.KubeSecretNameYataiDeploymentSharedEnv

	secret, err := secretGetter(ctx, yataiSystemNamespace, yataiDeploymentSharedEnvSecretName)
	if err != nil {
		if k8serrors.IsNotFound(err) {
			err = errors.Wrapf(err, "secret %s not found in namespace %s", yataiDeploymentSharedEnvSecretName, yataiSystemNamespace)
		}
		return
	}

	namespaces_ = string(secret.Data[consts.EnvBentoDeploymentNamespaces])
	if namespaces_ == "" {
		namespaces = []string{consts.DefaultKubeNamespaceBentoDeployment}
	} else {
		namespaces = strings.Split(namespaces_, ",")
	}

	return
}

type DockerRegistryConfig struct {
	BentoRepositoryName string `yaml:"bento_repository_name"`
	ModelRepositoryName string `yaml:"model_repository_name"`
	Server              string `yaml:"server"`
	InClusterServer     string `yaml:"in_cluster_server"`
	Username            string `yaml:"username"`
	Password            string `yaml:"password"`
	Secure              bool   `yaml:"secure"`
}

func GetDockerRegistryConfig(ctx context.Context, secretGetter func(ctx context.Context, namespace, name string) (*corev1.Secret, error)) (conf *DockerRegistryConfig, err error) {
	conf = &DockerRegistryConfig{}
	conf.BentoRepositoryName = os.Getenv(consts.EnvDockerRegistryBentoRepositoryName)
	conf.ModelRepositoryName = os.Getenv(consts.EnvDockerRegistryModelRepositoryName)
	conf.Server = os.Getenv(consts.EnvDockerRegistryServer)
	conf.InClusterServer = os.Getenv(consts.EnvDockerRegistryInClusterServer)
	conf.Username = os.Getenv(consts.EnvDockerRegistryUsername)
	conf.Password = os.Getenv(consts.EnvDockerRegistryPassword)
	conf.Secure = os.Getenv(consts.EnvDockerRegistrySecure) == "true"

	if conf.Server == "" {
		yataiSystemNamespace := GetYataiSystemNamespaceFromEnv()
		yataiImageBuilderSharedEnvSecretName := consts.KubeSecretNameYataiImageBuilderSharedEnv

		var secret *corev1.Secret

		secret, err = secretGetter(ctx, yataiSystemNamespace, yataiImageBuilderSharedEnvSecretName)
		if err != nil {
			if k8serrors.IsNotFound(err) {
				err = errors.Wrapf(err, "secret %s not found in namespace %s", yataiImageBuilderSharedEnvSecretName, yataiSystemNamespace)
			}
			return
		}

		conf.BentoRepositoryName = string(secret.Data[consts.EnvDockerRegistryBentoRepositoryName])
		conf.ModelRepositoryName = string(secret.Data[consts.EnvDockerRegistryModelRepositoryName])
		conf.Server = string(secret.Data[consts.EnvDockerRegistryServer])
		conf.InClusterServer = string(secret.Data[consts.EnvDockerRegistryInClusterServer])
		conf.Username = string(secret.Data[consts.EnvDockerRegistryUsername])
		conf.Password = string(secret.Data[consts.EnvDockerRegistryPassword])
		conf.Secure = string(secret.Data[consts.EnvDockerRegistrySecure]) == "true"

	}

	if conf.Server == "" {
		err = errors.Wrapf(errors.New("not found"), "the environment variable %s is not set", consts.EnvDockerRegistryServer)
	}

	return
}

type YataiConfig struct {
	Endpoint    string `yaml:"endpoint"`
	ClusterName string `yaml:"cluster_name"`
	ApiToken    string `yaml:"api_token"`
}

func GetYataiConfig(ctx context.Context, secretGetter func(ctx context.Context, namespace, name string) (*corev1.Secret, error), yataiComponentName string, ignoreEnv bool) (conf *YataiConfig, err error) {
	conf = &YataiConfig{}
	if !ignoreEnv {
		conf.Endpoint = os.Getenv(consts.EnvYataiEndpoint)
		conf.ClusterName = os.Getenv(consts.EnvYataiClusterName)
		conf.ApiToken = os.Getenv(consts.EnvYataiApiToken)
	}

	yataiSystemNamespace := GetYataiSystemNamespaceFromEnv()

	if conf.Endpoint == "" {
		var secret *corev1.Secret
		secret, err = secretGetter(ctx, yataiSystemNamespace, consts.KubeSecretNameYataiCommonEnv)
		if err != nil {
			if k8serrors.IsNotFound(err) {
				err = errors.Wrapf(err, "secret %s not found in namespace %s", consts.KubeSecretNameYataiCommonEnv, yataiSystemNamespace)
			}
			return
		}
		conf.Endpoint = string(secret.Data[consts.EnvYataiEndpoint])
		conf.ClusterName = string(secret.Data[consts.EnvYataiClusterName])
	}

	if conf.ApiToken == "" {
		var secret *corev1.Secret
		var secretName string
		var secretNamespace string
		if yataiComponentName == consts.YataiImageBuilderComponentName {
			secretName = consts.KubeSecretNameYataiImageBuilderEnv
			secretNamespace, err = GetYataiImageBuilderNamespace(ctx, secretGetter)
			if err != nil {
				err = errors.Wrapf(err, "failed to get namespace for %s", yataiComponentName)
				return
			}
		} else if yataiComponentName == consts.YataiDeploymentComponentName {
			secretName = consts.KubeSecretNameYataiDeploymentEnv
			secretNamespace, err = GetYataiDeploymentNamespace(ctx, secretGetter)
			if err != nil {
				err = errors.Wrapf(err, "failed to get namespace for %s", yataiComponentName)
				return
			}
		} else {
			err = errors.Errorf("invalid yatai component name %s", yataiComponentName)
			return
		}
		secret, err = secretGetter(ctx, secretNamespace, secretName)
		if err != nil {
			if k8serrors.IsNotFound(err) {
				err = errors.Errorf("the secret %s in namespace %s does not exist", secretName, secretNamespace)
			} else {
				err = errors.Wrapf(err, "failed to get secret %s in namespace %s", secretName, secretNamespace)
			}
			return
		}
		conf.ApiToken = string(secret.Data[consts.EnvYataiApiToken])
	}

	return
}

// if key found in environ return value else return fallback
func getEnv(key, fallback string) string {
	if value, ok := os.LookupEnv(key); ok {
		return value
	}
	return fallback
}

type InternalImages struct {
	BentoDownloader    string
	Curl               string
	Kaniko             string
	MetricsTransformer string
	Buildkit           string
	BuildkitRootless   string
	Buildah            string
}

func GetInternalImages() (conf *InternalImages) {
	conf = &InternalImages{}
	conf.BentoDownloader = getEnv(consts.EnvInternalImagesBentoDownloader, consts.InternalImagesBentoDownloaderDefault)
	conf.Curl = getEnv(consts.EnvInternalImagesCurl, consts.InternalImagesCurlDefault)
	conf.Kaniko = getEnv(consts.EnvInternalImagesKaniko, consts.InternalImagesKanikoDefault)
	conf.MetricsTransformer = getEnv(consts.EnvInternalImagesMetricsTransformer, consts.InternalImagesMetricsTransformerDefault)
	conf.Buildkit = getEnv(consts.EnvInternalImagesBuildkit, consts.InternalImagesBuildkitDefault)
	conf.BuildkitRootless = getEnv(consts.EnvInternalImagesBuildkitRootless, consts.InternalImagesBuildkitRootlessDefault)
	conf.Buildah = getEnv(consts.EnvInternalImagesBuildah, consts.InternalImagesBuildahDefault)

	return
}
