package helm

import (
	"fmt"
	"log"
	"os"

	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/chart/loader"
	"helm.sh/helm/v3/pkg/cli"
	"helm.sh/helm/v3/pkg/registry" // [NEW] Required for OCI
	"helm.sh/helm/v3/pkg/storage/driver"
	"k8s.io/cli-runtime/pkg/genericclioptions"
)

type Config struct {
	Namespace   string
	ReleaseName string
	RepoURL     string
	ChartName   string
	Version     string
	Values      map[string]interface{}
}

func Apply(cfg Config) error {
	settings := cli.New()

	// 1. Initialize Action Config
	actionConfig := new(action.Configuration)
	getter := genericclioptions.NewConfigFlags(false)

	if err := actionConfig.Init(getter, cfg.Namespace, os.Getenv("HELM_DRIVER"), log.Printf); err != nil {
		return fmt.Errorf("failed to init helm config: %w", err)
	}

	// 2. [NEW] Initialize OCI Registry Client
	// This tells Helm how to talk to ghcr.io, docker.io, etc.
	registryClient, err := registry.NewClient(
		registry.ClientOptDebug(true),
		registry.ClientOptEnableCache(true),
		registry.ClientOptCredentialsFile(settings.RegistryConfig), // Uses ~/.config/helm/registry/config.json
	)
	if err != nil {
		return fmt.Errorf("failed to init registry client: %w", err)
	}
	actionConfig.RegistryClient = registryClient

	// 3. Setup Install Action
	client := action.NewInstall(actionConfig)
	client.Version = cfg.Version
	client.Namespace = cfg.Namespace
	client.ReleaseName = cfg.ReleaseName
	client.CreateNamespace = true

	if cfg.RepoURL != "" {
		client.RepoURL = cfg.RepoURL
	}

	// 4. Locate Chart (Now supports oci:// because RegistryClient is set)
	cp, err := client.ChartPathOptions.LocateChart(cfg.ChartName, settings)
	if err != nil {
		return fmt.Errorf("failed to locate chart %s: %w", cfg.ChartName, err)
	}

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

	// 5. Install or Upgrade
	histClient := action.NewHistory(actionConfig)
	histClient.Max = 1

	if _, err := histClient.Run(cfg.ReleaseName); err == driver.ErrReleaseNotFound {
		fmt.Printf("Installing OCI Release %s...\n", cfg.ReleaseName)
		_, err := client.Run(chart, cfg.Values)
		return err
	} else if err != nil {
		return err
	}

	fmt.Printf("Upgrading OCI Release %s...\n", cfg.ReleaseName)
	upgrade := action.NewUpgrade(actionConfig)
	upgrade.Version = cfg.Version
	upgrade.Namespace = cfg.Namespace
	// Important: Upgrade also needs the RegistryClient, but it shares 'actionConfig'
	// so it is already set up.
	if cfg.RepoURL != "" {
		upgrade.RepoURL = cfg.RepoURL
	}
	_, err = upgrade.Run(cfg.ReleaseName, chart, cfg.Values)
	return err
}

func Uninstall(cfg Config) error {
	settings := cli.New()

	// 1. Initialize Action Config (Same as Apply)
	actionConfig := new(action.Configuration)
	getter := genericclioptions.NewConfigFlags(false)
	if err := actionConfig.Init(getter, cfg.Namespace, os.Getenv("HELM_DRIVER"), log.Printf); err != nil {
		return fmt.Errorf("failed to init helm config: %w", err)
	}

	// 2. Initialize OCI Registry Client (Crucial for OCI charts)
	registryClient, err := registry.NewClient(
		registry.ClientOptDebug(true),
		registry.ClientOptEnableCache(true),
		registry.ClientOptCredentialsFile(settings.RegistryConfig),
	)
	if err != nil {
		return fmt.Errorf("failed to init registry client: %w", err)
	}
	actionConfig.RegistryClient = registryClient

	// 3. Run Uninstall
	client := action.NewUninstall(actionConfig)
	// Don't fail if it's already gone
	_, err = client.Run(cfg.ReleaseName)
	if err != nil && err != driver.ErrReleaseNotFound {
		return fmt.Errorf("failed to uninstall release: %w", err)
	}

	fmt.Printf("✅ Uninstalled Release %s\n", cfg.ReleaseName)
	return nil
}
