package message

import (
	"encoding/json"
	"testing"

	helmv2 "github.com/fluxcd/helm-controller/api/v2"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestConvertToFluxCDHelmRelease_PreservesVersionSourceAndValues(t *testing.T) {
	cfg := HelmReleaseConfig{
		Version: "1.2.3",
		SourceRef: &HelmReleaseSourceRef{
			Kind:      "HelmRepository",
			Name:      "byoc-observability-charts",
			Namespace: "ops-system",
		},
		ExpectedDigest: "sha256:abc",
		Values: map[string]interface{}{
			"image": map[string]interface{}{
				"tag": "v1",
			},
		},
	}

	hr, err := ConvertToFluxCDHelmRelease("alertmanager-regional", "observability", "alertmanager", cfg)
	require.NoError(t, err)

	assert.Equal(t, "alertmanager-regional", hr.Spec.ReleaseName)
	assert.Equal(t, "observability", hr.Spec.TargetNamespace)
	assert.Equal(t, "alertmanager", hr.Spec.Chart.Spec.Chart)
	assert.Equal(t, "1.2.3", hr.Spec.Chart.Spec.Version)
	assert.Equal(t, "HelmRepository", hr.Spec.Chart.Spec.SourceRef.Kind)
	assert.Equal(t, "byoc-observability-charts", hr.Spec.Chart.Spec.SourceRef.Name)
	assert.Equal(t, "ops-system", hr.Spec.Chart.Spec.SourceRef.Namespace)
	assert.Equal(t, "sha256:abc", hr.Annotations[HelmReleaseExpectedDigestAnnotation])
	assert.NotNil(t, hr.Spec.Values)
}

func TestConvertFromFluxCDHelmRelease_PreservesVersionSourceAndDigest(t *testing.T) {
	hr, err := ConvertToFluxCDHelmRelease("self-vector", "self-monitoring", "vector", HelmReleaseConfig{
		Version:        "0.4.0",
		ExpectedDigest: "sha256:def",
	})
	require.NoError(t, err)

	component, cfg := ConvertFromFluxCDHelmRelease(hr)

	assert.Equal(t, "vector", component)
	assert.Equal(t, "0.4.0", cfg.Version)
	assert.Equal(t, "sha256:def", cfg.ExpectedDigest)
	require.NotNil(t, cfg.SourceRef)
	assert.Equal(t, "o11y-helm-repo", cfg.SourceRef.Name)
}

func TestConvertFluxCDStatusToSummary_IncludesFailureReasonAndVersions(t *testing.T) {
	status := helmv2.HelmReleaseStatus{
		ObservedGeneration:    3,
		LastAttemptedRevision: "1.2.4",
		LastReleaseRevision:   7,
		HelmChart:             "ops-system/alertmanager-regional",
		Failures:              2,
		History: helmv2.Snapshots{
			&helmv2.Snapshot{
				ChartVersion: "1.2.3",
				Status:       "deployed",
			},
		},
		Conditions: []metav1.Condition{{
			Type:    "Ready",
			Status:  metav1.ConditionFalse,
			Reason:  "UpgradeFailed",
			Message: "upgrade failed",
		}},
	}

	summary := ConvertFluxCDStatusToSummary(status)

	assert.Equal(t, int64(3), summary.ObservedGeneration)
	assert.Equal(t, "1.2.4", summary.LastAttemptedRevision)
	assert.Equal(t, "1.2.3", summary.LastAppliedRevision)
	assert.Equal(t, 7, summary.LastReleaseRevision)
	assert.Equal(t, int64(2), summary.Failures)
	assert.Equal(t, "UpgradeFailed", summary.LastFailureReason)
}

func TestHelmReleaseConfig_JSONUsesCamelCase(t *testing.T) {
	cfg := HelmReleaseConfig{
		DependsOn: []string{"o11y-crds"},
		SourceRef: &HelmReleaseSourceRef{
			Kind:      "HelmRepository",
			Name:      "observability-charts",
			Namespace: "ops-system",
			URL:       "oci://552185537300.dkr.ecr.us-west-2.amazonaws.com/observability-charts",
		},
		ExpectedDigest: "sha256:abc",
	}

	raw, err := json.Marshal(cfg)
	require.NoError(t, err)

	assert.Contains(t, string(raw), `"dependsOn"`)
	assert.Contains(t, string(raw), `"sourceRef"`)
	assert.Contains(t, string(raw), `"expectedDigest"`)
	assert.Contains(t, string(raw), `"url"`)
	assert.NotContains(t, string(raw), `"depends_on"`)
	assert.NotContains(t, string(raw), `"source_ref"`)
	assert.NotContains(t, string(raw), `"expected_digest"`)
}

func TestDiffHelmReleaseResp_JSONUsesCamelCase(t *testing.T) {
	resp := DiffHelmReleaseResp{
		RenderedDiff: HelmReleaseRenderedDiff{
			Changed:        true,
			ResolvedDigest: "sha256:resolved",
		},
		SourceResolution: HelmReleaseSourceResolution{
			SourceKind:      "HelmRepository",
			SourceName:      "observability-charts",
			SourceNamespace: "ops-system",
			ExpectedDigest:  "sha256:expected",
			ResolvedDigest:  "sha256:resolved",
		},
		SafeToApply:     false,
		BlockingReasons: []string{"digest mismatch"},
	}

	raw, err := json.Marshal(resp)
	require.NoError(t, err)

	for _, field := range []string{"renderedDiff", "resolvedDigest", "sourceResolution", "sourceKind", "sourceName", "sourceNamespace", "expectedDigest", "safeToApply", "blockingReasons"} {
		assert.Contains(t, string(raw), `"`+field+`"`)
	}
	for _, field := range []string{"rendered_diff", "resolved_digest", "source_resolution", "source_kind", "source_name", "source_namespace", "expected_digest", "safe_to_apply", "blocking_reasons"} {
		assert.NotContains(t, string(raw), `"`+field+`"`)
	}
}
