package opsservice

import (
	"context"
	"fmt"
	"strings"

	"github.com/tidbcloud/observability/pkg/common/message"
)

type helmReleaseDiffRunner interface {
	Diff(context.Context, message.DiffHelmReleaseReq) (message.HelmReleaseRenderedDiff, error)
}

func (s *HelmReleaseService) getDiffRunner() helmReleaseDiffRunner {
	if s.diffRunner != nil {
		return s.diffRunner
	}
	return newCommandHelmReleaseDiffRunner()
}

func (s *HelmReleaseService) DiffHelmRelease(ctx context.Context, req message.DiffHelmReleaseReq) (message.DiffHelmReleaseResp, error) {
	resp := message.DiffHelmReleaseResp{
		Name:      req.Name,
		Namespace: req.Namespace,
		Component: req.Component,
		SourceResolution: message.HelmReleaseSourceResolution{
			Chart:          req.Component,
			Version:        req.Config.Version,
			ExpectedDigest: req.Config.ExpectedDigest,
		},
	}
	if req.Config.SourceRef != nil {
		resp.SourceResolution.SourceKind = req.Config.SourceRef.Kind
		resp.SourceResolution.SourceName = req.Config.SourceRef.Name
		resp.SourceResolution.SourceNamespace = req.Config.SourceRef.Namespace
	}

	resp.BlockingReasons = append(resp.BlockingReasons, validateExactChartVersion(req.Config.Version)...)
	resp.BlockingReasons = append(resp.BlockingReasons, validateSourceRefURL(req.Config.SourceRef)...)
	if req.AdoptionMode {
		resp.Warnings = append(resp.Warnings, "adoption mode requires metadata-only live resource ownership patches before reconcile is enabled")
	}
	if len(resp.BlockingReasons) > 0 {
		resp.SafeToApply = false
		return resp, nil
	}

	renderedDiff, err := s.getDiffRunner().Diff(ctx, req)
	if err != nil {
		return message.DiffHelmReleaseResp{}, fmt.Errorf("failed to diff HelmRelease: %w", err)
	}
	resp.RenderedDiff = renderedDiff
	resp.SourceResolution.ResolvedDigest = renderedDiff.ResolvedDigest

	if req.Config.ExpectedDigest != "" && renderedDiff.ResolvedDigest != "" && req.Config.ExpectedDigest != renderedDiff.ResolvedDigest {
		resp.BlockingReasons = append(resp.BlockingReasons, fmt.Sprintf("expected digest %s does not match resolved digest %s", req.Config.ExpectedDigest, renderedDiff.ResolvedDigest))
	}
	if renderedDiff.Changed {
		resp.Warnings = append(resp.Warnings, "rendered chart differs from live state")
	}

	resp.SafeToApply = len(resp.BlockingReasons) == 0
	return resp, nil
}

func validateExactChartVersion(version string) []string {
	if version == "" || version == "latest" || strings.ContainsAny(version, "<>xX*") || strings.HasPrefix(version, "^") || strings.HasPrefix(version, "~") {
		return []string{"config.version must be an exact chart version"}
	}
	return nil
}

func validateSourceRefURL(sourceRef *message.HelmReleaseSourceRef) []string {
	if sourceRef == nil || sourceRef.URL == "" {
		return []string{"config.sourceRef.url is required for helm diff"}
	}
	return nil
}
