package sdk

import (
	"strconv"

	"github.com/portainer/portainer/pkg/libhelm/options"
	"github.com/portainer/portainer/pkg/libhelm/release"
	"github.com/rs/zerolog/log"
	"helm.sh/helm/v4/pkg/action"
	releasev1 "helm.sh/helm/v4/pkg/release/v1"
)

// Get implements the HelmPackageManager interface by using the Helm SDK to get a release.
// It returns a Release.
func (hspm *HelmSDKPackageManager) Get(getOptions options.GetOptions) (*release.Release, error) {
	log.Debug().
		Str("context", "HelmClient").
		Str("namespace", getOptions.Namespace).
		Str("name", getOptions.Name).
		Msg("Get Helm release")

	actionConfig := new(action.Configuration)
	err := hspm.initActionConfig(actionConfig, getOptions.Namespace, getOptions.KubernetesClusterAccess)

	if err != nil {
		log.Error().
			Str("context", "HelmClient").
			Str("namespace", getOptions.Namespace).
			Err(err).Msg("Failed to initialise helm configuration")
		return nil, err
	}

	statusClient, err := hspm.initStatusClient(actionConfig, getOptions)
	if err != nil {
		log.Error().
			Str("context", "HelmClient").
			Str("namespace", getOptions.Namespace).
			Err(err).Msg("Failed to initialise helm status client")
		return nil, err
	}

	release, err := statusClient.Run(getOptions.Name)
	if err != nil {
		log.Error().
			Str("context", "HelmClient").
			Str("namespace", getOptions.Namespace).
			Err(err).Msg("Failed to query helm chart")
		return nil, err
	}

	values, err := hspm.getValues(getOptions)
	if err != nil {
		// error is already logged in getValuesFromStatus
		return nil, err
	}

	v1Release, err := releaserToV1Release(release)
	if err != nil {
		log.Error().
			Str("context", "HelmClient").
			Str("namespace", getOptions.Namespace).
			Str("name", getOptions.Name).
			Err(err).Msg("Failed to convert release to v1")
		return nil, err
	}
	return convert(v1Release, values), nil
}

// Helm status is just an extended helm get command with resources added on (when flagged), so use the status client with the optional show resources flag
// https://github.com/helm/helm/blob/0199b748aaea3091852d16687c9f9f809061777c/pkg/action/get.go#L40-L47
// https://github.com/helm/helm/blob/0199b748aaea3091852d16687c9f9f809061777c/pkg/action/status.go#L48-L82
func (hspm *HelmSDKPackageManager) initStatusClient(actionConfig *action.Configuration, getOptions options.GetOptions) (*action.Status, error) {
	statusClient := action.NewStatus(actionConfig)
	if getOptions.Revision > 0 {
		statusClient.Version = getOptions.Revision
	}

	return statusClient, nil
}

func convert(releasev1 *releasev1.Release, values release.Values) *release.Release {
	resources, err := parseResources(releasev1.Info.Resources)
	if err != nil {
		log.Warn().
			Str("context", "HelmClient").
			Str("namespace", releasev1.Namespace).
			Str("name", releasev1.Name).
			Err(err).Msg("Failed to parse resources")
	}

	// Parse stack ID from annotations -> int
	stackID := 0
	if releasev1.Chart != nil && releasev1.Chart.Metadata != nil {
		if s, ok := releasev1.Chart.Metadata.Annotations[StackIDAnnotation]; ok && s != "" {
			if id, err := strconv.Atoi(s); err == nil {
				stackID = id
			} else {
				log.Warn().
					Str("context", "HelmClient").
					Str("namespace", releasev1.Namespace).
					Str("name", releasev1.Name).
					Err(err).Msg("Failed to parse stack id from annotations")
			}
		}
	}
	release := &release.Release{
		Name:      releasev1.Name,
		Namespace: releasev1.Namespace,
		Version:   releasev1.Version,
		Info: &release.Info{
			Status:       release.Status(releasev1.Info.Status),
			Notes:        releasev1.Info.Notes,
			Resources:    resources,
			Description:  releasev1.Info.Description,
			LastDeployed: releasev1.Info.LastDeployed,
		},
		Manifest: releasev1.Manifest,
		Chart: release.Chart{
			Metadata: &release.Metadata{
				Name:       releasev1.Chart.Metadata.Name,
				Version:    releasev1.Chart.Metadata.Version,
				AppVersion: releasev1.Chart.Metadata.AppVersion,
			},
		},
		Values:         values,
		ChartReference: extractChartReferenceAnnotations(releasev1.Chart.Metadata.Annotations),
		StackID:        stackID,
	}

	return release
}
