package handler

import (
	"archive/tar"
	"compress/gzip"
	"context"
	"encoding/base64"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	rbdmodel "github.com/goodrain/rainbond/api/model"
	"github.com/goodrain/rainbond/db"
	dbmodel "github.com/goodrain/rainbond/db/model"
	"github.com/goodrain/rainbond/pkg/component/k8s"
	"github.com/goodrain/rainbond/pkg/helm"
	"github.com/goodrain/rainbond/util/constants"
	httputil "github.com/goodrain/rainbond/util/http"
	"helm.sh/helm/v3/pkg/chart"
	helmrelease "helm.sh/helm/v3/pkg/release"
	k8sapierrors "k8s.io/apimachinery/pkg/api/errors"
	k8sapimeta "k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"sigs.k8s.io/yaml"
)

// HelmReleaseHandler handles Helm release operations for a tenant namespace.
// repoFile and repoCache are package-level variables from api/handler/helm.go:60-64,
// accessible here since this file is in the same package.
type HelmReleaseHandler struct{}

const (
	HelmReleaseSourceStore  = "store"
	HelmReleaseSourceRepo   = "repo"
	HelmReleaseSourceOCI    = "oci"
	HelmReleaseSourceUpload = "upload"
)

// HelmReleaseInstallRequest describes all supported install sources.
type HelmReleaseInstallRequest struct {
	SourceType        string `json:"source_type"`
	Namespace         string `json:"namespace"`
	RepoName          string `json:"repo_name"`
	RepoURL           string `json:"repo_url"`
	Chart             string `json:"chart"`
	ChartName         string `json:"chart_name"`
	ChartURL          string `json:"chart_url"`
	Version           string `json:"version"`
	ReleaseName       string `json:"release_name"`
	Values            string `json:"values"`
	Username          string `json:"username"`
	Password          string `json:"password"`
	EventID           string `json:"event_id"`
	AllowChartReplace bool   `json:"allow_chart_replace"`
}

type HelmReleaseChartPreview struct {
	Name        string            `json:"name"`
	Version     string            `json:"version"`
	Description string            `json:"description"`
	Icon        string            `json:"icon"`
	Keywords    []string          `json:"keywords"`
	AppVersion  string            `json:"app_version"`
	Values      map[string]string `json:"values"`
	Readme      string            `json:"readme"`
}

type HelmReleaseSummary struct {
	Name         string `json:"name"`
	Chart        string `json:"chart"`
	ChartVersion string `json:"chart_version"`
	AppVersion   string `json:"app_version"`
	Status       string `json:"status"`
	Version      int    `json:"version"`
	Namespace    string `json:"namespace"`
	Updated      string `json:"updated"`
}

type HelmReleaseHistoryItem struct {
	Revision     int    `json:"revision"`
	Chart        string `json:"chart"`
	ChartVersion string `json:"chart_version"`
	AppVersion   string `json:"app_version"`
	Status       string `json:"status"`
	Description  string `json:"description"`
	Updated      string `json:"updated"`
}

type HelmReleaseDetailSummary struct {
	Name         string `json:"name"`
	Namespace    string `json:"namespace"`
	Status       string `json:"status"`
	Chart        string `json:"chart"`
	ChartVersion string `json:"chart_version"`
	AppVersion   string `json:"app_version"`
	Revision     int    `json:"revision"`
	Description  string `json:"description"`
	Updated      string `json:"updated"`
	Values       string `json:"values"`
}

type HelmReleaseDetail struct {
	Summary   *HelmReleaseDetailSummary `json:"summary"`
	Workloads []NsResourceInfo          `json:"workloads"`
	Services  []NsResourceInfo          `json:"services"`
	Others    []NsResourceInfo          `json:"others"`
	History   []*HelmReleaseHistoryItem `json:"history"`
}

type HelmReleaseRollbackRequest struct {
	Revision int `json:"revision"`
}

var helmReleaseResourceTargets = []schema.GroupVersionResource{
	{Group: "apps", Version: "v1", Resource: "deployments"},
	{Group: "apps", Version: "v1", Resource: "statefulsets"},
	{Group: "apps", Version: "v1", Resource: "daemonsets"},
	{Group: "batch", Version: "v1", Resource: "jobs"},
	{Group: "batch", Version: "v1", Resource: "cronjobs"},
	{Group: "", Version: "v1", Resource: "services"},
	{Group: "", Version: "v1", Resource: "configmaps"},
	{Group: "", Version: "v1", Resource: "secrets"},
	{Group: "", Version: "v1", Resource: "serviceaccounts"},
	{Group: "", Version: "v1", Resource: "persistentvolumeclaims"},
	{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles"},
	{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"},
	{Group: "networking.k8s.io", Version: "v1", Resource: "ingresses"},
	{Group: "autoscaling", Version: "v2", Resource: "horizontalpodautoscalers"},
	{Group: "gateway.networking.k8s.io", Version: "v1beta1", Resource: "gateways"},
	{Group: "gateway.networking.k8s.io", Version: "v1beta1", Resource: "httproutes"},
	{Group: "rollouts.kruise.io", Version: "v1alpha1", Resource: "rollouts"},
}

func (r *HelmReleaseInstallRequest) Normalize() {
	r.SourceType = strings.TrimSpace(r.SourceType)
	if r.SourceType == "" {
		r.SourceType = HelmReleaseSourceStore
	}
	r.Namespace = strings.TrimSpace(r.Namespace)
	r.Chart = strings.TrimSpace(firstNonEmpty(r.Chart, r.ChartName))
	r.ChartName = strings.TrimSpace(firstNonEmpty(r.ChartName, r.Chart))
	r.ChartURL = strings.TrimSpace(r.ChartURL)
	r.RepoName = strings.TrimSpace(r.RepoName)
	r.RepoURL = strings.TrimSpace(r.RepoURL)
	r.ReleaseName = strings.TrimSpace(r.ReleaseName)
	r.EventID = strings.TrimSpace(r.EventID)
}

func (r *HelmReleaseInstallRequest) Validate() error {
	if r.ReleaseName == "" {
		return fmt.Errorf("release_name is required")
	}
	switch r.SourceType {
	case HelmReleaseSourceStore:
		if r.RepoName == "" {
			return fmt.Errorf("repo_name is required for store source")
		}
		if r.Chart == "" {
			return fmt.Errorf("chart is required for store source")
		}
	case HelmReleaseSourceRepo:
		hasRepoChart := r.RepoURL != "" && r.ChartName != ""
		hasDirectChartURL := r.ChartURL != ""
		if !hasRepoChart && !hasDirectChartURL {
			return fmt.Errorf("repo source requires repo_url and chart_name, or chart_url")
		}
	case HelmReleaseSourceOCI:
		if !strings.HasPrefix(r.ChartURL, "oci://") {
			return fmt.Errorf("oci source requires chart_url with oci:// prefix")
		}
	case HelmReleaseSourceUpload:
		if r.EventID == "" {
			return fmt.Errorf("event_id is required for upload source")
		}
	default:
		return fmt.Errorf("unsupported source_type %q", r.SourceType)
	}
	return nil
}

func (r *HelmReleaseInstallRequest) ValidateForPreview() error {
	switch r.SourceType {
	case HelmReleaseSourceStore:
		if r.RepoName == "" {
			return fmt.Errorf("repo_name is required for store source")
		}
		if r.Chart == "" && r.ChartName == "" {
			return fmt.Errorf("chart is required for store source")
		}
	case HelmReleaseSourceRepo:
		if strings.TrimSpace(firstNonEmpty(r.ChartURL, r.ChartName)) == "" {
			return fmt.Errorf("chart_url is required for repo source")
		}
	case HelmReleaseSourceOCI:
		if !strings.HasPrefix(strings.TrimSpace(r.ChartURL), "oci://") {
			return fmt.Errorf("oci source requires chart_url with oci:// prefix")
		}
	case HelmReleaseSourceUpload:
		if r.EventID == "" {
			return fmt.Errorf("event_id is required for upload source")
		}
	default:
		return fmt.Errorf("unsupported source_type %q", r.SourceType)
	}
	return nil
}

func (r *HelmReleaseRollbackRequest) Validate() error {
	if r.Revision <= 0 {
		return fmt.Errorf("revision must be greater than 0")
	}
	return nil
}

func (h *HelmReleaseHandler) resolveNamespace(tenantName, namespace string) (string, error) {
	if strings.TrimSpace(namespace) != "" {
		return strings.TrimSpace(namespace), nil
	}
	tenant, err := db.GetManager().TenantDao().GetTenantIDByName(tenantName)
	if err != nil {
		return "", fmt.Errorf("tenant %s not found: %v", tenantName, err)
	}
	return helmReleaseNamespace(tenant), nil
}

func (h *HelmReleaseHandler) newHelm(tenantName, namespace string) (*helm.Helm, error) {
	resolvedNamespace, err := h.resolveNamespace(tenantName, namespace)
	if err != nil {
		return nil, err
	}
	return helm.NewHelm(resolvedNamespace, repoFile, repoCache)
}

// ListReleases returns all Helm releases in the tenant's namespace.
func (h *HelmReleaseHandler) ListReleases(tenantName, namespace string) ([]*HelmReleaseSummary, error) {
	hc, err := h.newHelm(tenantName, namespace)
	if err != nil {
		return nil, err
	}
	releases, err := hc.ListReleases()
	if err != nil {
		return nil, err
	}
	summaries := make([]*HelmReleaseSummary, 0, len(releases))
	for _, release := range releases {
		summaries = append(summaries, summarizeHelmRelease(release))
	}
	return summaries, nil
}

// GetReleaseHistory returns the Helm revision history for the given release.
func (h *HelmReleaseHandler) GetReleaseHistory(tenantName, releaseName, namespace string) ([]*HelmReleaseHistoryItem, error) {
	hc, err := h.newHelm(tenantName, namespace)
	if err != nil {
		return nil, err
	}
	history, err := hc.History(releaseName)
	if err != nil {
		return nil, err
	}
	return summarizeHelmReleaseHistory(history), nil
}

// GetReleaseDetail returns the Helm release status, values, history and related namespace resources.
func (h *HelmReleaseHandler) GetReleaseDetail(tenantName, releaseName, namespace string) (*HelmReleaseDetail, error) {
	hc, err := h.newHelm(tenantName, namespace)
	if err != nil {
		return nil, err
	}
	release, err := hc.Status(releaseName)
	if err != nil {
		return nil, err
	}
	history, err := hc.History(releaseName)
	if err != nil {
		return nil, err
	}
	resources, err := h.listReleaseResources(tenantName, releaseName, release.Namespace)
	if err != nil {
		return nil, err
	}
	workloads, services, others := splitHelmReleaseResources(resources)
	return &HelmReleaseDetail{
		Summary:   summarizeHelmReleaseDetail(release),
		Workloads: workloads,
		Services:  services,
		Others:    others,
		History:   summarizeHelmReleaseHistory(history),
	}, nil
}

// InstallRelease installs a Helm release from store, repo, OCI or uploaded chart sources.
func (h *HelmReleaseHandler) InstallRelease(tenantName string, req HelmReleaseInstallRequest) (*helmrelease.Release, error) {
	req.Normalize()
	if err := req.Validate(); err != nil {
		return nil, err
	}
	hc, err := h.newHelm(tenantName, req.Namespace)
	if err != nil {
		return nil, err
	}
	switch req.SourceType {
	case HelmReleaseSourceStore:
		return hc.InstallFromRepo(req.RepoName, req.Chart, req.Version, req.ReleaseName, req.Values)
	case HelmReleaseSourceRepo:
		chartRef := firstNonEmpty(req.ChartURL, req.ChartName)
		return hc.InstallFromReference(chartRef, req.RepoURL, req.Version, req.ReleaseName, req.Values, req.Username, req.Password)
	case HelmReleaseSourceOCI:
		return hc.InstallFromReference(req.ChartURL, "", req.Version, req.ReleaseName, req.Values, req.Username, req.Password)
	case HelmReleaseSourceUpload:
		chartPath, chartVersion, err := GetUploadChartPathAndVersion(req.EventID)
		if err != nil {
			return nil, err
		}
		version := req.Version
		if version == "" {
			version = chartVersion
		}
		return hc.InstallFromChartPath(chartPath, version, req.ReleaseName, req.Values)
	default:
		return nil, fmt.Errorf("unsupported source_type %q", req.SourceType)
	}
}

// PreviewChart resolves chart metadata, readme and values files before installation.
func (h *HelmReleaseHandler) PreviewChart(tenantName string, req HelmReleaseInstallRequest) (*HelmReleaseChartPreview, error) {
	req.Normalize()
	if err := req.ValidateForPreview(); err != nil {
		return nil, err
	}
	hc, err := h.newHelm(tenantName, req.Namespace)
	if err != nil {
		return nil, err
	}

	ch, chartPath, version, err := h.loadTargetChart(hc, req)
	if err != nil {
		return nil, wrapHelmChartPreviewSourceError(err)
	}

	values, readme, err := readChartPreviewFiles(chartPath)
	if err != nil {
		return nil, wrapHelmChartPreviewSourceError(err)
	}
	if version == "" && ch != nil && ch.Metadata != nil {
		version = ch.Metadata.Version
	}
	preview := &HelmReleaseChartPreview{
		Version: version,
		Values:  values,
		Readme:  readme,
	}
	if ch != nil && ch.Metadata != nil {
		preview.Name = ch.Metadata.Name
		preview.Description = ch.Metadata.Description
		preview.Icon = ch.Metadata.Icon
		preview.Keywords = ch.Metadata.Keywords
		preview.AppVersion = ch.Metadata.AppVersion
	}
	return preview, nil
}

// UpgradeRelease upgrades an existing Helm release using a newly specified chart source.
func (h *HelmReleaseHandler) UpgradeRelease(tenantName, releaseName string, req HelmReleaseInstallRequest) (*helmrelease.Release, error) {
	req.Normalize()
	req.ReleaseName = releaseName
	if err := req.Validate(); err != nil {
		return nil, err
	}
	hc, err := h.newHelm(tenantName, req.Namespace)
	if err != nil {
		return nil, err
	}
	currentRelease, err := hc.Status(releaseName)
	if err != nil {
		return nil, err
	}
	targetChart, chartPath, version, err := h.loadTargetChart(hc, req)
	if err != nil {
		return nil, err
	}
	if err := validateUpgradeChartName(currentRelease, targetChart, req.AllowChartReplace); err != nil {
		return nil, err
	}
	return hc.UpgradeFromChartPath(chartPath, version, releaseName, req.Values)
}

// RollbackRelease rolls the given release back to a previous revision.
func (h *HelmReleaseHandler) RollbackRelease(tenantName, releaseName, namespace string, revision int) error {
	hc, err := h.newHelm(tenantName, namespace)
	if err != nil {
		return err
	}
	return hc.Rollback(releaseName, revision)
}

// UninstallRelease uninstalls a Helm release from the tenant's namespace.
func (h *HelmReleaseHandler) UninstallRelease(tenantName, releaseName, namespace string) error {
	hc, err := h.newHelm(tenantName, namespace)
	if err != nil {
		return err
	}
	return hc.Uninstall(releaseName)
}

var helmReleaseHandler *HelmReleaseHandler

// GetHelmReleaseHandler returns the singleton HelmReleaseHandler.
func GetHelmReleaseHandler() *HelmReleaseHandler {
	if helmReleaseHandler == nil {
		helmReleaseHandler = &HelmReleaseHandler{}
	}
	return helmReleaseHandler
}

func summarizeHelmRelease(release *helmrelease.Release) *HelmReleaseSummary {
	summary := &HelmReleaseSummary{}
	if release == nil {
		return summary
	}

	summary.Name = release.Name
	summary.Version = release.Version
	summary.Namespace = release.Namespace

	if release.Chart != nil && release.Chart.Metadata != nil {
		summary.Chart = release.Chart.Metadata.Name
		summary.ChartVersion = release.Chart.Metadata.Version
		summary.AppVersion = release.Chart.Metadata.AppVersion
	}

	if release.Info != nil {
		summary.Status = release.Info.Status.String()
		if !release.Info.LastDeployed.Time.IsZero() {
			summary.Updated = release.Info.LastDeployed.Time.UTC().Format(time.RFC3339)
		}
	}

	return summary
}

func summarizeHelmReleaseDetail(release *helmrelease.Release) *HelmReleaseDetailSummary {
	summary := &HelmReleaseDetailSummary{}
	if release == nil {
		return summary
	}
	summary.Name = release.Name
	summary.Namespace = release.Namespace
	summary.Revision = release.Version
	summary.Values = marshalHelmReleaseValues(release.Config)
	if release.Chart != nil && release.Chart.Metadata != nil {
		summary.Chart = release.Chart.Metadata.Name
		summary.ChartVersion = release.Chart.Metadata.Version
		summary.AppVersion = release.Chart.Metadata.AppVersion
	}
	if release.Info != nil {
		summary.Status = release.Info.Status.String()
		summary.Description = release.Info.Description
		if !release.Info.LastDeployed.Time.IsZero() {
			summary.Updated = release.Info.LastDeployed.Time.UTC().Format(time.RFC3339)
		}
	}
	return summary
}

func summarizeHelmReleaseHistory(history helm.ReleaseHistory) []*HelmReleaseHistoryItem {
	items := make([]*HelmReleaseHistoryItem, 0, len(history))
	for _, item := range history {
		chartName := item.ChartName
		chartVersion := item.ChartVersion
		if chartName == "" || chartVersion == "" {
			fallbackChartName, fallbackChartVersion := splitHelmChartLabel(item.Chart)
			if chartName == "" {
				chartName = fallbackChartName
			}
			if chartVersion == "" {
				chartVersion = fallbackChartVersion
			}
		}
		items = append(items, &HelmReleaseHistoryItem{
			Revision:     item.Revision,
			Chart:        chartName,
			ChartVersion: chartVersion,
			AppVersion:   item.AppVersion,
			Status:       item.Status,
			Description:  item.Description,
			Updated:      formatHelmReleaseTime(item.Updated.Time),
		})
	}
	return items
}

func marshalHelmReleaseValues(values map[string]interface{}) string {
	if len(values) == 0 {
		return ""
	}
	data, err := yaml.Marshal(values)
	if err != nil {
		return ""
	}
	return string(data)
}

func isHelmReleaseResource(labels map[string]string, releaseName string) bool {
	if len(labels) == 0 || strings.TrimSpace(releaseName) == "" {
		return false
	}
	return labels[constants.ResourceManagedByLabel] == "Helm" &&
		labels[constants.ResourceInstanceLabel] == releaseName
}

func splitHelmReleaseResources(resources []NsResourceInfo) ([]NsResourceInfo, []NsResourceInfo, []NsResourceInfo) {
	workloads := make([]NsResourceInfo, 0)
	services := make([]NsResourceInfo, 0)
	others := make([]NsResourceInfo, 0)
	for _, resource := range resources {
		switch resource.Kind {
		case rbdmodel.Deployment, rbdmodel.StateFulSet, "DaemonSet", rbdmodel.Job, rbdmodel.CronJob, rbdmodel.Rollout:
			workloads = append(workloads, resource)
		case rbdmodel.Service:
			services = append(services, resource)
		default:
			others = append(others, resource)
		}
	}
	return workloads, services, others
}

func (h *HelmReleaseHandler) listReleaseResources(tenantName, releaseName, namespace string) ([]NsResourceInfo, error) {
	ns := strings.TrimSpace(namespace)
	if ns == "" {
		resolvedNamespace, err := h.resolveNamespace(tenantName, namespace)
		if err != nil {
			return nil, err
		}
		ns = resolvedNamespace
	}
	resources := make([]NsResourceInfo, 0, 16)
	for _, gvr := range helmReleaseResourceTargets {
		list, err := k8s.Default().DynamicClient.Resource(gvr).Namespace(ns).List(context.Background(), metav1.ListOptions{})
		if err != nil {
			if k8sapimeta.IsNoMatchError(err) || k8sapierrors.IsNotFound(err) {
				continue
			}
			return nil, err
		}
		for _, item := range list.Items {
			if !isHelmReleaseResource(item.GetLabels(), releaseName) {
				continue
			}
			resources = append(resources, toNsResourceInfo(item))
		}
	}
	sort.Slice(resources, func(i, j int) bool {
		if resources[i].Kind == resources[j].Kind {
			return resources[i].Name < resources[j].Name
		}
		return resources[i].Kind < resources[j].Kind
	})
	return resources, nil
}

func firstNonEmpty(values ...string) string {
	for _, value := range values {
		if strings.TrimSpace(value) != "" {
			return value
		}
	}
	return ""
}

func helmReleaseNamespace(tenant *dbmodel.Tenants) string {
	if tenant == nil {
		return ""
	}
	if strings.TrimSpace(tenant.Namespace) != "" {
		return tenant.Namespace
	}
	return tenant.UUID
}

func formatHelmReleaseTime(ts time.Time) string {
	if ts.IsZero() {
		return ""
	}
	return ts.UTC().Format(time.RFC3339)
}

func splitHelmChartLabel(chart string) (string, string) {
	value := strings.TrimSpace(chart)
	if value == "" {
		return "", ""
	}
	for index := len(value) - 1; index >= 0; index-- {
		if value[index] != '-' {
			continue
		}
		if index+1 >= len(value) || (value[index+1] < '0' || value[index+1] > '9') {
			continue
		}
		return value[:index], value[index+1:]
	}
	return value, ""
}

func validateUpgradeChartName(currentRelease *helmrelease.Release, targetChart *chart.Chart, allowChartReplace bool) error {
	if currentRelease == nil || currentRelease.Chart == nil || currentRelease.Chart.Metadata == nil {
		return nil
	}
	if targetChart == nil || targetChart.Metadata == nil {
		return nil
	}
	currentName := strings.TrimSpace(currentRelease.Chart.Metadata.Name)
	targetName := strings.TrimSpace(targetChart.Metadata.Name)
	if currentName == "" || targetName == "" {
		return nil
	}
	if currentName != targetName {
		if allowChartReplace {
			return nil
		}
		return fmt.Errorf("upgrade chart name %q does not match current release chart %q", targetName, currentName)
	}
	return nil
}

func (h *HelmReleaseHandler) loadTargetChart(hc *helm.Helm, req HelmReleaseInstallRequest) (*chart.Chart, string, string, error) {
	switch req.SourceType {
	case HelmReleaseSourceStore:
		chartRef := fmt.Sprintf("%s/%s", req.RepoName, req.Chart)
		return hc.LoadChartFromReference(chartRef, "", req.Version, "", "")
	case HelmReleaseSourceRepo:
		chartRef := firstNonEmpty(req.ChartURL, req.ChartName)
		return hc.LoadChartFromReference(chartRef, req.RepoURL, req.Version, req.Username, req.Password)
	case HelmReleaseSourceOCI:
		return hc.LoadChartFromReference(req.ChartURL, "", req.Version, req.Username, req.Password)
	case HelmReleaseSourceUpload:
		chartPath, version, err := GetUploadChartPathAndVersion(req.EventID)
		if err != nil {
			return nil, "", "", err
		}
		ch, err := hc.LoadChartFromPath(chartPath)
		return ch, chartPath, version, err
	default:
		return nil, "", "", fmt.Errorf("unsupported source_type %q", req.SourceType)
	}
}

func wrapHelmChartPreviewSourceError(err error) error {
	if err == nil {
		return nil
	}
	if _, ok := err.(httputil.ErrBadRequest); ok {
		return err
	}
	return httputil.NewErrBadRequest(err)
}

func readChartPreviewFiles(chartPath string) (map[string]string, string, error) {
	stat, err := os.Stat(chartPath)
	if err != nil {
		return nil, "", err
	}
	if stat.IsDir() {
		return readChartPreviewFilesFromDir(chartPath)
	}
	return readChartPreviewFilesFromArchive(chartPath)
}

func readChartPreviewFilesFromDir(chartPath string) (map[string]string, string, error) {
	values := make(map[string]string)
	readme := ""
	err := filepath.Walk(chartPath, func(currentPath string, info os.FileInfo, walkErr error) error {
		if walkErr != nil || info == nil || info.IsDir() {
			return walkErr
		}
		relPath, err := filepath.Rel(chartPath, currentPath)
		if err != nil {
			return err
		}
		content, err := os.ReadFile(currentPath)
		if err != nil {
			return err
		}
		if strings.HasSuffix(relPath, "README.md") && readme == "" {
			readme = base64.StdEncoding.EncodeToString(content)
		}
		if strings.HasSuffix(relPath, "values.yaml") {
			values[filepath.ToSlash(relPath)] = base64.StdEncoding.EncodeToString(content)
		}
		return nil
	})
	return values, readme, err
}

func readChartPreviewFilesFromArchive(chartPath string) (map[string]string, string, error) {
	file, err := os.Open(chartPath)
	if err != nil {
		return nil, "", err
	}
	defer file.Close()
	gzr, err := gzip.NewReader(file)
	if err != nil {
		return nil, "", err
	}
	defer gzr.Close()

	values := make(map[string]string)
	readme := ""
	tr := tar.NewReader(gzr)
	for {
		header, err := tr.Next()
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, "", err
		}
		if header.FileInfo().IsDir() {
			continue
		}
		content, err := io.ReadAll(tr)
		if err != nil {
			return nil, "", err
		}
		if strings.HasSuffix(header.Name, "README.md") && readme == "" {
			readme = base64.StdEncoding.EncodeToString(content)
		}
		if strings.HasSuffix(header.Name, "values.yaml") {
			values[header.Name] = base64.StdEncoding.EncodeToString(content)
		}
	}
	return values, readme, nil
}