// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package helm

import (
	"context"
	"fmt"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"

	"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
	"github.com/hashicorp/terraform-plugin-framework/attr"
	"github.com/hashicorp/terraform-plugin-framework/datasource"
	"github.com/hashicorp/terraform-plugin-framework/diag"
	"github.com/hashicorp/terraform-plugin-framework/path"
	"github.com/hashicorp/terraform-plugin-framework/provider"
	"github.com/hashicorp/terraform-plugin-framework/provider/schema"
	"github.com/hashicorp/terraform-plugin-framework/resource"
	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
	"github.com/hashicorp/terraform-plugin-framework/types"
	"github.com/hashicorp/terraform-plugin-log/tflog"
	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/cli"
	"helm.sh/helm/v3/pkg/registry"
	"helm.sh/helm/v3/pkg/storage/driver"
)

var _ provider.Provider = &HelmProvider{}

func New(version string) func() provider.Provider {
	return func() provider.Provider {
		return &HelmProvider{
			version: version,
		}
	}
}

// Meta contains the client configuration for the provider
type Meta struct {
	providerData   *HelmProvider
	Data           *HelmProviderModel
	Settings       *cli.EnvSettings
	RegistryClient *registry.Client
	HelmDriver     string
	// Experimental feature toggles
	Experiments map[string]bool
	Mutex       sync.Mutex
}

// HelmProviderModel contains the configuration for the provider
type HelmProviderModel struct {
	Debug                types.Bool              `tfsdk:"debug"`
	PluginsPath          types.String            `tfsdk:"plugins_path"`
	RegistryConfigPath   types.String            `tfsdk:"registry_config_path"`
	RepositoryConfigPath types.String            `tfsdk:"repository_config_path"`
	RepositoryCache      types.String            `tfsdk:"repository_cache"`
	HelmDriver           types.String            `tfsdk:"helm_driver"`
	BurstLimit           types.Int64             `tfsdk:"burst_limit"`
	Kubernetes           types.Object            `tfsdk:"kubernetes"`
	Registries           types.List              `tfsdk:"registries"`
	Experiments          *ExperimentsConfigModel `tfsdk:"experiments"`
}

// ExperimentsConfigModel configures the experiments that are enabled or disabled
type ExperimentsConfigModel struct {
	Manifest types.Bool `tfsdk:"manifest"`
}

// RegistryConfigModel configures an OCI registry
type RegistryConfigModel struct {
	URL      types.String `tfsdk:"url"`
	Username types.String `tfsdk:"username"`
	Password types.String `tfsdk:"password"`
}

// KubernetesConfigModel configures a Kubernetes client
type KubernetesConfigModel struct {
	Host                  types.String     `tfsdk:"host"`
	Username              types.String     `tfsdk:"username"`
	Password              types.String     `tfsdk:"password"`
	Insecure              types.Bool       `tfsdk:"insecure"`
	TLSServerName         types.String     `tfsdk:"tls_server_name"`
	ClientCertificate     types.String     `tfsdk:"client_certificate"`
	ClientKey             types.String     `tfsdk:"client_key"`
	ClusterCACertificate  types.String     `tfsdk:"cluster_ca_certificate"`
	ConfigPaths           types.List       `tfsdk:"config_paths"`
	ConfigPath            types.String     `tfsdk:"config_path"`
	ConfigContext         types.String     `tfsdk:"config_context"`
	ConfigContextAuthInfo types.String     `tfsdk:"config_context_auth_info"`
	ConfigContextCluster  types.String     `tfsdk:"config_context_cluster"`
	Token                 types.String     `tfsdk:"token"`
	ProxyURL              types.String     `tfsdk:"proxy_url"`
	Exec                  *ExecConfigModel `tfsdk:"exec"`
}

// ExecConfigModel configures an external command to configure the Kubernetes client
type ExecConfigModel struct {
	APIVersion types.String `tfsdk:"api_version"`
	Command    types.String `tfsdk:"command"`
	Env        types.Map    `tfsdk:"env"`
	Args       types.List   `tfsdk:"args"`
}

// HelmProvider is the top level provider struct
type HelmProvider struct {
	meta    *Meta
	version string
}

func (p *HelmProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
	resp.TypeName = "helm"
	resp.Version = p.version
}

// ///////////////////////            	START OF SCHEMA CREATION               ///////////////////////////////
// Defines attributes that are avaiable in the provider
func (p *HelmProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
	resp.Schema = schema.Schema{
		Description: "Schema to define attributes that are available in the provider",
		Attributes: map[string]schema.Attribute{
			"debug": schema.BoolAttribute{
				Description: "Debug indicates whether or not Helm is running in Debug mode.",
				Optional:    true,
			},
			"plugins_path": schema.StringAttribute{
				Description: "The path to the helm plugins directory",
				Optional:    true,
			},
			"registry_config_path": schema.StringAttribute{
				Description: "The path to the registry config file",
				Optional:    true,
			},
			"repository_config_path": schema.StringAttribute{
				Description: "The path to the file containing repository names and URLs",
				Optional:    true,
			},
			"repository_cache": schema.StringAttribute{
				Description: "The path to the file containing cached repository indexes",
				Optional:    true,
			},
			"helm_driver": schema.StringAttribute{
				Description: "The backend storage driver. Values are: configmap, secret, memory, sql",
				Optional:    true,
				Validators: []validator.String{
					stringvalidator.OneOf(strings.ToLower(driver.MemoryDriverName),
						strings.ToLower(driver.ConfigMapsDriverName),
						strings.ToLower(driver.SecretsDriverName),
						strings.ToLower(driver.SQLDriverName)),
				},
			},

			"burst_limit": schema.Int64Attribute{
				Optional:    true,
				Description: "Helm burst limit. Increase this if you have a cluster with many CRDs",
			},
			"kubernetes": schema.SingleNestedAttribute{
				Optional:    true,
				Description: "Kubernetes Configuration",
				Attributes:  kubernetesResourceSchema(),
			},
			"registries": schema.ListNestedAttribute{
				Optional:    true,
				Description: "RegistryClient configuration.",
				NestedObject: schema.NestedAttributeObject{
					Attributes: registriesResourceSchema(),
				},
			},
			"experiments": schema.SingleNestedAttribute{
				Optional:    true,
				Description: "Enable and disable experimental features.",
				Attributes:  experimentsSchema(),
			},
		},
	}
}

func experimentsSchema() map[string]schema.Attribute {
	return map[string]schema.Attribute{
		"manifest": schema.BoolAttribute{
			Optional:    true,
			Description: "Enable full diff by storing the rendered manifest in the state.",
		},
	}
}

func registriesResourceSchema() map[string]schema.Attribute {
	return map[string]schema.Attribute{
		"url": schema.StringAttribute{
			Required:    true,
			Description: "OCI URL in form of oci://host:port or oci://host",
		},
		"username": schema.StringAttribute{
			Required:    true,
			Description: "The username to use for the OCI HTTP basic authentication when accessing the Kubernetes master endpoint.",
		},
		"password": schema.StringAttribute{
			Required:    true,
			Description: "The password to use for the OCI HTTP basic authentication when accessing the Kubernetes master endpoint.",
		},
	}
}

func kubernetesResourceSchema() map[string]schema.Attribute {
	return map[string]schema.Attribute{
		"host": schema.StringAttribute{
			Optional:    true,
			Description: "The hostname (in form of URI) of kubernetes master",
		},
		"username": schema.StringAttribute{
			Optional:    true,
			Description: "The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint",
		},
		"password": schema.StringAttribute{
			Optional:    true,
			Description: "The password to use for HTTP basic authentication when accessing the Kubernetes master endpoint.",
		},
		"insecure": schema.BoolAttribute{
			Optional:    true,
			Description: "Whether server should be accessed without verifying the TLS certificate.",
		},
		"tls_server_name": schema.StringAttribute{
			Optional:    true,
			Description: "Server name passed to the server for SNI and is used in the client to check server certificates against.",
		},
		"client_certificate": schema.StringAttribute{
			Optional:    true,
			Description: "PEM-encoded client certificate for TLS authentication.",
		},
		"client_key": schema.StringAttribute{
			Optional:    true,
			Description: "PEM-encoded client certificate key for TLS authentication.",
		},
		"cluster_ca_certificate": schema.StringAttribute{
			Optional:    true,
			Description: "PEM-encoded root certificates bundle for TLS authentication.",
		},
		"config_paths": schema.ListAttribute{
			Optional:    true,
			ElementType: types.StringType,
			Description: "A list of paths to kube config files. Can be set with KUBE_CONFIG_PATHS environment variable.",
		},
		"config_path": schema.StringAttribute{
			Optional:    true,
			Description: "Path to the kube config file. Can be set with KUBE_CONFIG_PATH.",
			Validators: []validator.String{
				stringvalidator.ConflictsWith(
					path.Root("kubernetes").AtName("config_paths").Expression(),
				),
			},
		},

		"config_context": schema.StringAttribute{
			Optional:    true,
			Description: "Context to choose from the config file. Can be sourced from KUBE_CTX.",
		},
		"config_context_auth_info": schema.StringAttribute{
			Optional:    true,
			Description: "Authentication info context of the kube config (name of the kubeconfig user, --user flag in kubectl). Can be sourced from KUBE_CTX_AUTH_INFO.",
		},
		"config_context_cluster": schema.StringAttribute{
			Optional:    true,
			Description: "Cluster context of the kube config (name of the kubeconfig cluster, --cluster flag in kubectl). Can be sourced from KUBE_CTX_CLUSTER.",
		},
		"token": schema.StringAttribute{
			Optional:    true,
			Description: "Token to authenticate a service account.",
		},
		"proxy_url": schema.StringAttribute{
			Optional:    true,
			Description: "URL to the proxy to be used for all API requests.",
		},
		"exec": schema.SingleNestedAttribute{
			Optional:    true,
			Description: "Exec configuration for Kubernetes authentication",
			Attributes:  execSchema(),
		},
	}
}

func execSchema() map[string]schema.Attribute {
	return map[string]schema.Attribute{
		"api_version": schema.StringAttribute{
			Required:    true,
			Description: "API version for the exec plugin.",
		},
		"command": schema.StringAttribute{
			Required:    true,
			Description: "Command to run for Kubernetes exec plugin",
		},
		"env": schema.MapAttribute{
			Optional:    true,
			ElementType: types.StringType,
			Description: "Environment variables for the exec plugin",
		},
		"args": schema.ListAttribute{
			Optional:    true,
			ElementType: types.StringType,
			Description: "Arguments for the exec plugin",
		},
	}
}

func execSchemaAttrTypes() map[string]attr.Type {
	return map[string]attr.Type{
		"api_version": types.StringType,
		"command":     types.StringType,
		"args":        types.ListType{ElemType: types.StringType},
		"env":         types.MapType{ElemType: types.StringType},
	}
}

/////////////////////     					END OF SCHEMA CREATION           ///////////////////////////////

// Setting up the provider, anything we need to get the provider running, probbaly authentication. like the api
func (p *HelmProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
	if req.ClientCapabilities.DeferralAllowed && !req.Config.Raw.IsFullyKnown() {
		resp.Deferred = &provider.Deferred{
			Reason: provider.DeferredReasonProviderConfigUnknown,
		}
	}

	pluginsPath := os.Getenv("HELM_PLUGINS_PATH")
	registryConfigPath := os.Getenv("HELM_REGISTRY_CONFIG_PATH")
	repositoryConfigPath := os.Getenv("HELM_REPOSITORY_CONFIG_PATH")
	repositoryCache := os.Getenv("HELM_REPOSITORY_CACHE")
	helmDriver := os.Getenv("HELM_DRIVER")
	burstLimitStr := os.Getenv("HELM_BURST_LIMIT")
	kubeHost := os.Getenv("KUBE_HOST")
	kubeUser := os.Getenv("KUBE_USER")
	kubePassword := os.Getenv("KUBE_PASSWORD")
	kubeInsecureStr := os.Getenv("KUBE_INSECURE")
	kubeTLSServerName := os.Getenv("KUBE_TLS_SERVER_NAME")
	kubeClientCert := os.Getenv("KUBE_CLIENT_CERT_DATA")
	kubeClientKey := os.Getenv("KUBE_CLIENT_KEY_DATA")
	kubeCaCert := os.Getenv("KUBE_CLUSTER_CA_CERT_DATA")
	kubeConfigPaths := os.Getenv("KUBE_CONFIG_PATHS")
	kubeConfigPath := os.Getenv("KUBE_CONFIG_PATH")
	kubeConfigContext := os.Getenv("KUBE_CTX")
	kubeConfigContextAuthInfo := os.Getenv("KUBE_CTX_AUTH_INFO")
	kubeConfigContextCluster := os.Getenv("KUBE_CTX_CLUSTER")
	kubeToken := os.Getenv("KUBE_TOKEN")
	kubeProxy := os.Getenv("KUBE_PROXY")

	// Initialize the HelmProviderModel with values from the config
	var config HelmProviderModel
	diags := req.Config.Get(ctx, &config)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	if !config.PluginsPath.IsNull() {
		pluginsPath = config.PluginsPath.ValueString()
	}
	if !config.RegistryConfigPath.IsNull() {
		registryConfigPath = config.RegistryConfigPath.ValueString()
	}
	if !config.RepositoryConfigPath.IsNull() {
		repositoryConfigPath = config.RepositoryConfigPath.ValueString()
	}
	if !config.RepositoryCache.IsNull() {
		repositoryCache = config.RepositoryCache.ValueString()
	}
	if !config.HelmDriver.IsNull() {
		helmDriver = config.HelmDriver.ValueString()
	}
	var burstLimit int64
	if burstLimitStr != "" {
		var err error
		burstLimit, err = strconv.ParseInt(burstLimitStr, 10, 64)
		if err != nil {
			resp.Diagnostics.AddError(
				"Invalid burst limit",
				fmt.Sprintf("Invalid burst limit value: %s", burstLimitStr),
			)
			return
		}
	}
	if !config.BurstLimit.IsNull() {
		burstLimit = config.BurstLimit.ValueInt64()
	}
	var kubeInsecure bool
	if kubeInsecureStr != "" {
		var err error
		kubeInsecure, err = strconv.ParseBool(kubeInsecureStr)
		if err != nil {
			resp.Diagnostics.AddError(
				"Invalid insecure value",
				fmt.Sprintf("Invalid insecure value: %s", kubeInsecureStr),
			)
			return
		}
	}

	var kubernetesConfig KubernetesConfigModel
	if !config.Kubernetes.IsNull() && !config.Kubernetes.IsUnknown() {
		diags := req.Config.GetAttribute(ctx, path.Root("kubernetes"), &kubernetesConfig)
		resp.Diagnostics.Append(diags...)
		if resp.Diagnostics.HasError() {
			return
		}
	}

	if !kubernetesConfig.Insecure.IsNull() {
		kubeInsecure = kubernetesConfig.Insecure.ValueBool()
	}
	var kubeConfigPathsList []attr.Value
	if !kubernetesConfig.Host.IsNull() {
		kubeHost = kubernetesConfig.Host.ValueString()
	}
	if !kubernetesConfig.Username.IsNull() {
		kubeUser = kubernetesConfig.Username.ValueString()
	}
	if !kubernetesConfig.Password.IsNull() {
		kubePassword = kubernetesConfig.Password.ValueString()
	}
	if !kubernetesConfig.TLSServerName.IsNull() {
		kubeTLSServerName = kubernetesConfig.TLSServerName.ValueString()
	}
	if !kubernetesConfig.ClientCertificate.IsNull() {
		kubeClientCert = kubernetesConfig.ClientCertificate.ValueString()
	}
	if !kubernetesConfig.ClientKey.IsNull() {
		kubeClientKey = kubernetesConfig.ClientKey.ValueString()
	}
	if !kubernetesConfig.ClusterCACertificate.IsNull() {
		kubeCaCert = kubernetesConfig.ClusterCACertificate.ValueString()
	}
	if kubeConfigPaths != "" {
		for _, path := range filepath.SplitList(kubeConfigPaths) {
			kubeConfigPathsList = append(kubeConfigPathsList, types.StringValue(path))
		}
	}
	if !kubernetesConfig.ConfigPaths.IsNull() {
		var paths []string
		diags = kubernetesConfig.ConfigPaths.ElementsAs(ctx, &paths, false)
		resp.Diagnostics.Append(diags...)
		for _, path := range paths {
			kubeConfigPathsList = append(kubeConfigPathsList, types.StringValue(path))
		}
	}
	if !kubernetesConfig.ConfigPath.IsNull() {
		kubeConfigPath = kubernetesConfig.ConfigPath.ValueString()
	}
	if !kubernetesConfig.ConfigContext.IsNull() {
		kubeConfigContext = kubernetesConfig.ConfigContext.ValueString()
	}
	if !kubernetesConfig.ConfigContextAuthInfo.IsNull() {
		kubeConfigContextAuthInfo = kubernetesConfig.ConfigContextAuthInfo.ValueString()
	}
	if !kubernetesConfig.ConfigContextCluster.IsNull() {
		kubeConfigContextCluster = kubernetesConfig.ConfigContextCluster.ValueString()
	}
	if !kubernetesConfig.Token.IsNull() {
		kubeToken = kubernetesConfig.Token.ValueString()
	}
	if !kubernetesConfig.ProxyURL.IsNull() {
		kubeProxy = kubernetesConfig.ProxyURL.ValueString()
	}
	tflog.Debug(ctx, "Config values after overrides", map[string]interface{}{
		"config": config,
	})
	debug := os.Getenv("HELM_DEBUG") == "true" || config.Debug.ValueBool()
	settings := cli.New()
	settings.Debug = debug
	if pluginsPath != "" {
		settings.PluginsDirectory = pluginsPath
	}
	if registryConfigPath != "" {
		settings.RegistryConfig = registryConfigPath
	}
	if repositoryConfigPath != "" {
		settings.RepositoryConfig = repositoryConfigPath
	}
	if repositoryCache != "" {
		settings.RepositoryCache = repositoryCache
	}
	tflog.Debug(ctx, "Helm settings initialized", map[string]interface{}{
		"settings": settings,
	})
	kubeConfigPathsListValue, diags := types.ListValue(types.StringType, kubeConfigPathsList)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	manifestExperiment := false
	if config.Experiments != nil {
		manifestExperiment = config.Experiments.Manifest.ValueBool()
	}

	var execAttrValue attr.Value = types.ObjectNull(execSchemaAttrTypes())

	if kubernetesConfig.Exec != nil {
		// Check if `api_version` and `command` are set (since they're required fields)
		if !kubernetesConfig.Exec.APIVersion.IsNull() && !kubernetesConfig.Exec.Command.IsNull() {
			execAttrValue = types.ObjectValueMust(execSchemaAttrTypes(), map[string]attr.Value{
				"api_version": types.StringValue(kubernetesConfig.Exec.APIVersion.ValueString()),
				"command":     types.StringValue(kubernetesConfig.Exec.Command.ValueString()),
				"args":        types.ListValueMust(types.StringType, kubernetesConfig.Exec.Args.Elements()),
				"env":         types.MapValueMust(types.StringType, kubernetesConfig.Exec.Env.Elements()),
			})
		}
	}

	kubernetesConfigObjectValue, diags := types.ObjectValue(map[string]attr.Type{
		"host":                     types.StringType,
		"username":                 types.StringType,
		"password":                 types.StringType,
		"insecure":                 types.BoolType,
		"tls_server_name":          types.StringType,
		"client_certificate":       types.StringType,
		"client_key":               types.StringType,
		"cluster_ca_certificate":   types.StringType,
		"config_paths":             types.ListType{ElemType: types.StringType},
		"config_path":              types.StringType,
		"config_context":           types.StringType,
		"config_context_auth_info": types.StringType,
		"config_context_cluster":   types.StringType,
		"token":                    types.StringType,
		"proxy_url":                types.StringType,
		"exec":                     types.ObjectType{AttrTypes: execSchemaAttrTypes()},
	}, map[string]attr.Value{
		"host":                     types.StringValue(kubeHost),
		"username":                 types.StringValue(kubeUser),
		"password":                 types.StringValue(kubePassword),
		"insecure":                 types.BoolValue(kubeInsecure),
		"tls_server_name":          types.StringValue(kubeTLSServerName),
		"client_certificate":       types.StringValue(kubeClientCert),
		"client_key":               types.StringValue(kubeClientKey),
		"cluster_ca_certificate":   types.StringValue(kubeCaCert),
		"config_paths":             kubeConfigPathsListValue,
		"config_path":              types.StringValue(kubeConfigPath),
		"config_context":           types.StringValue(kubeConfigContext),
		"config_context_auth_info": types.StringValue(kubeConfigContextAuthInfo),
		"config_context_cluster":   types.StringValue(kubeConfigContextCluster),
		"token":                    types.StringValue(kubeToken),
		"proxy_url":                types.StringValue(kubeProxy),
		"exec":                     execAttrValue,
	})
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	meta := &Meta{
		Data: &HelmProviderModel{
			Debug:                types.BoolValue(debug),
			PluginsPath:          types.StringValue(pluginsPath),
			RegistryConfigPath:   types.StringValue(registryConfigPath),
			RepositoryConfigPath: types.StringValue(repositoryConfigPath),
			RepositoryCache:      types.StringValue(repositoryCache),
			HelmDriver:           types.StringValue(helmDriver),
			BurstLimit:           types.Int64Value(burstLimit),
			Kubernetes:           kubernetesConfigObjectValue,
			Experiments: &ExperimentsConfigModel{
				Manifest: types.BoolValue(manifestExperiment),
			},
		},
		Settings:   settings,
		HelmDriver: helmDriver,
		Experiments: map[string]bool{
			"manifest": manifestExperiment,
		},
	}
	registryClient, err := registry.NewClient()
	if err != nil {
		resp.Diagnostics.AddError(
			"Registry client initialization failed",
			fmt.Sprintf("Unable to create Helm registry client: %s", err),
		)
		return
	}

	meta.RegistryClient = registryClient
	if !config.Registries.IsUnknown() {
		var registryConfigs []RegistryConfigModel
		diags := config.Registries.ElementsAs(ctx, &registryConfigs, false)
		resp.Diagnostics.Append(diags...)
		if resp.Diagnostics.HasError() {
			return
		}
		for _, r := range registryConfigs {
			if r.URL.IsNull() || r.Username.IsNull() || r.Password.IsNull() {
				resp.Diagnostics.AddError(
					"OCI Registry login failed",
					"Registry URL, Username, or Password is null",
				)
				return
			}

			err := OCIRegistryPerformLogin(ctx, meta, meta.RegistryClient, r.URL.ValueString(), r.Username.ValueString(), r.Password.ValueString())
			if err != nil {
				resp.Diagnostics.AddError(
					"OCI Registry login failed",
					err.Error(),
				)
				return
			}
		}
	} else {
		tflog.Debug(ctx, "No registry configurations found")
	}
	resp.DataSourceData = meta
	resp.ResourceData = meta

	tflog.Debug(ctx, "Configure method completed successfully")
}

func (p *HelmProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
	return []func() datasource.DataSource{
		NewHelmTemplate,
	}
}

func (p *HelmProvider) Resources(ctx context.Context) []func() resource.Resource {
	return []func() resource.Resource{
		NewHelmRelease,
	}
}

func OCIRegistryLogin(ctx context.Context, meta *Meta, actionConfig *action.Configuration, registryClient *registry.Client, repository, chartName, username, password string) diag.Diagnostics {
	var diags diag.Diagnostics

	actionConfig.RegistryClient = registryClient

	var ociURL string
	if registry.IsOCI(repository) {
		ociURL = repository
	} else if registry.IsOCI(chartName) {
		ociURL = chartName
	}

	if ociURL == "" {
		return diags
	}

	if username != "" && password != "" {
		err := OCIRegistryPerformLogin(ctx, meta, registryClient, ociURL, username, password)
		if err != nil {
			diags.AddError(
				"OCI Registry Login Failed",
				fmt.Sprintf("Failed to log in to OCI registry %q: %s", ociURL, err.Error()),
			)
		}
	}

	return diags
}

// registryClient = client used to comm with the registry, oci urls, un, and pw used for authentication
func OCIRegistryPerformLogin(ctx context.Context, meta *Meta, registryClient *registry.Client, ociURL, username, password string) error {
	loggedInOCIRegistries := make(map[string]string)
	// getting the oci url, and extracting the host.
	u, err := url.Parse(ociURL)
	if err != nil {
		return fmt.Errorf("could not parse OCI registry URL: %v", err)
	}
	meta.Mutex.Lock()
	defer meta.Mutex.Unlock()
	if _, ok := loggedInOCIRegistries[u.Host]; ok {
		tflog.Info(ctx, fmt.Sprintf("Already logged into OCI registry %q", u.Host))
		return nil
	}
	// Now we perform the login, with the provided username and password by calling the login method
	err = registryClient.Login(u.Host, registry.LoginOptBasicAuth(username, password))
	if err != nil {
		return fmt.Errorf("could not login to OCI registry %q: %v", u.Host, err)
	}
	loggedInOCIRegistries[u.Host] = ""
	tflog.Info(ctx, fmt.Sprintf("Logged into OCI registry %q", u.Host))
	return nil
}

// GetHelmConfiguration retrieves the Helm configuration for a given namespace
func (m *Meta) GetHelmConfiguration(ctx context.Context, namespace string) (*action.Configuration, error) {
	if m == nil {
		tflog.Error(ctx, "Meta is nil")
		return nil, fmt.Errorf("Meta is nil")
	}

	tflog.Info(context.Background(), "[INFO] GetHelmConfiguration start")
	actionConfig := new(action.Configuration)
	kc, err := m.NewKubeConfig(ctx, namespace)
	if err != nil {
		return nil, err
	}
	if err := actionConfig.Init(kc, namespace, m.HelmDriver, func(format string, v ...interface{}) {
		tflog.Info(context.Background(), fmt.Sprintf(format, v...))
	}); err != nil {
		return nil, err
	}
	tflog.Info(context.Background(), "[INFO] GetHelmConfiguration success")
	// returning the initializing action.Configuration object
	return actionConfig, nil
}
