package real

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/ocdp/cluster-service/internal/domain/entity"
	"github.com/ocdp/cluster-service/internal/domain/repository"
	domainservice "github.com/ocdp/cluster-service/internal/domain/service"
	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/chart/loader"
	"helm.sh/helm/v3/pkg/cli"
	"helm.sh/helm/v3/pkg/release"
	"helm.sh/helm/v3/pkg/storage/driver"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/discovery/cached/memory"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/restmapper"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

// HelmClient 真实的 Helm 客户端实现
type HelmClient struct {
	settings *cli.EnvSettings
}

// NewHelmClient 创建真实的 Helm 客户端
func NewHelmClient() repository.HelmClient {
	return &HelmClient{
		settings: cli.New(),
	}
}

// getActionConfig 获取 Helm action configuration
func (h *HelmClient) getActionConfig(cluster *entity.Cluster, namespace string) (*action.Configuration, func(), error) {
	actionConfig := new(action.Configuration)

	// 创建临时 kubeconfig 文件
	kubeconfigContent := cluster.GetKubeConfig()
	tmpDir, err := os.MkdirTemp("", "helm-kubeconfig-*")
	if err != nil {
		return nil, nil, fmt.Errorf("failed to create temp dir: %w", err)
	}
	cleanup := func() {
		_ = os.RemoveAll(tmpDir)
	}

	kubeconfigPath := filepath.Join(tmpDir, "kubeconfig")
	if err := os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0600); err != nil {
		cleanup()
		return nil, nil, fmt.Errorf("failed to write kubeconfig: %w", err)
	}

	// 使用 kubeconfig 初始化 action config
	if err := actionConfig.Init(
		&kubeconfigGetter{kubeconfigPath: kubeconfigPath, namespace: namespace},
		namespace,
		os.Getenv("HELM_DRIVER"), // storage driver: configmap, secret, memory
		func(format string, v ...interface{}) {
			// Log function
		},
	); err != nil {
		cleanup()
		return nil, nil, fmt.Errorf("failed to initialize action config: %w", err)
	}

	return actionConfig, cleanup, nil
}

// kubeconfigGetter implements RESTClientGetter
type kubeconfigGetter struct {
	kubeconfigPath string
	namespace      string
}

func (k *kubeconfigGetter) ToRESTConfig() (*rest.Config, error) {
	return clientcmd.BuildConfigFromFlags("", k.kubeconfigPath)
}

func (k *kubeconfigGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
	config, err := k.ToRESTConfig()
	if err != nil {
		return nil, err
	}
	discoveryClient := discovery.NewDiscoveryClientForConfigOrDie(config)
	// Wrap in a memory cache
	return memory.NewMemCacheClient(discoveryClient), nil
}

func (k *kubeconfigGetter) ToRESTMapper() (meta.RESTMapper, error) {
	discoveryClient, err := k.ToDiscoveryClient()
	if err != nil {
		return nil, err
	}
	mapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient)
	return mapper, nil
}

func (k *kubeconfigGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig {
	overrides := &clientcmd.ConfigOverrides{}
	if k.namespace != "" {
		overrides.Context = clientcmdapi.Context{Namespace: k.namespace}
	}
	return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		&clientcmd.ClientConfigLoadingRules{ExplicitPath: k.kubeconfigPath},
		overrides,
	)
}

// Install 安装 Helm Chart
func (h *HelmClient) Install(ctx context.Context, cluster *entity.Cluster, instance *entity.Instance) error {
	actionConfig, cleanup, err := h.getActionConfig(cluster, instance.Namespace)
	if err != nil {
		return err
	}
	defer cleanup()

	install := action.NewInstall(actionConfig)
	install.ReleaseName = instance.Name
	install.Namespace = instance.Namespace
	install.CreateNamespace = true
	install.Wait = true
	install.Timeout = helmOperationTimeout()

	// 加载 Chart（从本地路径或 OCI registry）
	// 这里简化处理，假设 chart 已经被拉取到本地
	chartPath := fmt.Sprintf("/tmp/charts/%s-%s.tgz", instance.Chart, instance.Version)

	chart, err := loader.Load(chartPath)
	if err != nil {
		return fmt.Errorf("failed to load chart: %w", err)
	}

	// 执行安装
	rel, err := install.Run(chart, instance.Values)
	if err != nil {
		return fmt.Errorf("failed to install release: %w", err)
	}

	// 更新 revision（状态由调用方根据操作结果设置）
	instance.Revision = rel.Version
	// 注意：不在这里设置 Status，让调用方通过 MarkSuccess/MarkFailure 来设置

	return nil
}

// Upgrade 升级 Helm Release
func (h *HelmClient) Upgrade(ctx context.Context, cluster *entity.Cluster, instance *entity.Instance) error {
	actionConfig, cleanup, err := h.getActionConfig(cluster, instance.Namespace)
	if err != nil {
		return err
	}
	defer cleanup()

	upgrade := action.NewUpgrade(actionConfig)
	upgrade.Namespace = instance.Namespace
	upgrade.ReuseValues = true
	upgrade.Wait = true
	upgrade.Timeout = helmOperationTimeout()

	// 加载 Chart
	chartPath := fmt.Sprintf("/tmp/charts/%s-%s.tgz", instance.Chart, instance.Version)

	chart, err := loader.Load(chartPath)
	if err != nil {
		return fmt.Errorf("failed to load chart: %w", err)
	}

	// 执行升级
	rel, err := upgrade.Run(instance.Name, chart, instance.Values)
	if err != nil {
		return fmt.Errorf("failed to upgrade release: %w", err)
	}

	// 更新 revision（状态由调用方根据操作结果设置）
	instance.Revision = rel.Version
	// 注意：不在这里设置 Status，让调用方通过 MarkSuccess/MarkFailure 来设置

	return nil
}

// Uninstall 卸载 Helm Release
func (h *HelmClient) Uninstall(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) error {
	actionConfig, cleanup, err := h.getActionConfig(cluster, namespace)
	if err != nil {
		return err
	}
	defer cleanup()

	uninstall := action.NewUninstall(actionConfig)
	uninstall.Wait = true
	uninstall.Timeout = helmOperationTimeout()

	_, err = uninstall.Run(releaseName)
	if err != nil {
		if errors.Is(err, driver.ErrReleaseNotFound) {
			return entity.ErrInstanceNotFound
		}
		return fmt.Errorf("failed to uninstall release: %w", err)
	}

	return nil
}

// Rollback 回滚 Helm Release
func (h *HelmClient) Rollback(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string, revision int) error {
	actionConfig, cleanup, err := h.getActionConfig(cluster, namespace)
	if err != nil {
		return err
	}
	defer cleanup()

	rollback := action.NewRollback(actionConfig)
	rollback.Version = revision
	rollback.Wait = true
	rollback.Timeout = helmOperationTimeout()

	if err := rollback.Run(releaseName); err != nil {
		return fmt.Errorf("failed to rollback release: %w", err)
	}

	return nil
}

func helmOperationTimeout() time.Duration {
	raw := os.Getenv("HELM_OPERATION_TIMEOUT")
	if raw == "" {
		return 15 * time.Minute
	}
	timeout, err := time.ParseDuration(raw)
	if err != nil || timeout <= 0 {
		return 15 * time.Minute
	}
	return timeout
}

// GetStatus 获取 Release 状态
func (h *HelmClient) GetStatus(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) (*entity.Instance, error) {
	actionConfig, cleanup, err := h.getActionConfig(cluster, namespace)
	if err != nil {
		return nil, err
	}
	defer cleanup()

	status := action.NewStatus(actionConfig)
	rel, err := status.Run(releaseName)
	if err != nil {
		return nil, fmt.Errorf("failed to get release status: %w", err)
	}

	return h.convertReleaseToInstance(rel), nil
}

// GetHistory 获取 Release 历史
func (h *HelmClient) GetHistory(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) ([]*entity.ReleaseHistory, error) {
	actionConfig, cleanup, err := h.getActionConfig(cluster, namespace)
	if err != nil {
		return nil, err
	}
	defer cleanup()

	history := action.NewHistory(actionConfig)
	history.Max = 256

	releases, err := history.Run(releaseName)
	if err != nil {
		return nil, fmt.Errorf("failed to get release history: %w", err)
	}

	result := make([]*entity.ReleaseHistory, 0, len(releases))
	for _, rel := range releases {
		result = append(result, &entity.ReleaseHistory{
			Revision:    rel.Version,
			Updated:     rel.Info.LastDeployed.Time,
			Status:      entity.InstanceStatus(rel.Info.Status),
			Chart:       rel.Chart.Metadata.Name,
			AppVersion:  rel.Chart.Metadata.AppVersion,
			Description: rel.Info.Description,
		})
	}

	return result, nil
}

// List 列出集群中的所有 Releases
func (h *HelmClient) List(ctx context.Context, cluster *entity.Cluster, namespace string) ([]*entity.Instance, error) {
	actionConfig, cleanup, err := h.getActionConfig(cluster, namespace)
	if err != nil {
		return nil, err
	}
	defer cleanup()

	list := action.NewList(actionConfig)
	if namespace == "" {
		list.AllNamespaces = true
	}

	releases, err := list.Run()
	if err != nil {
		return nil, fmt.Errorf("failed to list releases: %w", err)
	}

	instances := make([]*entity.Instance, 0, len(releases))
	for _, rel := range releases {
		instances = append(instances, h.convertReleaseToInstance(rel))
	}

	return instances, nil
}

// GetValues 获取 Release 的 values
func (h *HelmClient) GetValues(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) (map[string]interface{}, error) {
	actionConfig, cleanup, err := h.getActionConfig(cluster, namespace)
	if err != nil {
		return nil, err
	}
	defer cleanup()

	getValues := action.NewGetValues(actionConfig)
	getValues.AllValues = true
	values, err := getValues.Run(releaseName)
	if err != nil {
		return nil, fmt.Errorf("failed to get values: %w", err)
	}

	return values, nil
}

// GetChartDefaultValues 从 chart 包中读取默认 values
func (h *HelmClient) GetChartDefaultValues(chartPath string) (map[string]interface{}, error) {
	chart, err := loader.Load(chartPath)
	if err != nil {
		return nil, fmt.Errorf("failed to load chart: %w", err)
	}
	vals := make(map[string]interface{})
	if chart.Values != nil {
		for k, v := range chart.Values {
			vals[k] = v
		}
	}
	return vals, nil
}

func (h *HelmClient) EstimateInstanceResources(ctx context.Context, cluster *entity.Cluster, instance *entity.Instance) (*repository.ResourceEstimate, error) {
	chartPath := fmt.Sprintf("/tmp/charts/%s-%s.tgz", instance.Chart, instance.Version)
	chart, err := loader.Load(chartPath)
	if err != nil {
		return nil, fmt.Errorf("failed to load chart: %w", err)
	}
	actionConfig := new(action.Configuration)
	actionConfig.Log = func(format string, v ...interface{}) {}

	install := action.NewInstall(actionConfig)
	install.ReleaseName = instance.Name
	if install.ReleaseName == "" {
		install.ReleaseName = "quota-precheck"
	}
	install.Namespace = instance.Namespace
	if install.Namespace == "" {
		install.Namespace = "default"
	}
	install.DryRun = true
	install.DryRunOption = "client"
	install.ClientOnly = true
	install.Replace = true
	install.SkipSchemaValidation = true

	values := instance.Values
	if values == nil {
		values = map[string]interface{}{}
	}
	release, err := install.RunWithContext(ctx, chart, values)
	if err != nil {
		return nil, fmt.Errorf("failed to render chart for quota estimate: %w", err)
	}
	return domainservice.EstimateRenderedManifestResources(release.Manifest)
}

// convertReleaseToInstance 转换 Helm Release 为 Instance
func (h *HelmClient) convertReleaseToInstance(rel *release.Release) *entity.Instance {
	return &entity.Instance{
		Name:      rel.Name,
		Namespace: rel.Namespace,
		Chart:     rel.Chart.Metadata.Name,
		Version:   rel.Chart.Metadata.Version,
		Status:    entity.InstanceStatus(rel.Info.Status),
		Revision:  rel.Version,
		Values:    rel.Config,
		UpdatedAt: rel.Info.LastDeployed.Time,
	}
}
