package tenantoverlay

import (
	"bytes"
	"encoding/json"
	"strings"
	"testing"
)

// TestCanonicalizePreservesAllStartFields verifies that triggerOrder AND other
// unknown FlowStart fields survive the canonicalizeFlowMetadata round-trip.
func TestCanonicalizePreservesAllStartFields(t *testing.T) {
	raw := []byte(`{"start":{"triggerType":"RecordBeforeSave","object":"Account","triggerOrder":650,"doesRequireRecordChangedToMeetCriteria":true,"filterFormula":"ISCHANGED(Name)"},"processType":"AutoLaunchedFlow"}`)

	out, err := canonicalizeFlowMetadata(raw)
	if err != nil {
		t.Fatalf("canonicalizeFlowMetadata returned error: %v", err)
	}

	var result struct {
		Start struct {
			TriggerOrder                           *float64 `json:"triggerOrder"`
			DoesRequireRecordChangedToMeetCriteria *bool    `json:"doesRequireRecordChangedToMeetCriteria"`
			FilterFormula                          *string  `json:"filterFormula"`
		} `json:"start"`
	}
	if err := json.Unmarshal(out, &result); err != nil {
		t.Fatalf("failed to unmarshal canonicalized output: %v", err)
	}
	if result.Start.TriggerOrder == nil || *result.Start.TriggerOrder != 650 {
		t.Fatalf("triggerOrder: got %v, want 650", result.Start.TriggerOrder)
	}
	if result.Start.DoesRequireRecordChangedToMeetCriteria == nil || !*result.Start.DoesRequireRecordChangedToMeetCriteria {
		t.Fatalf("doesRequireRecordChangedToMeetCriteria: got %v, want true", result.Start.DoesRequireRecordChangedToMeetCriteria)
	}
	if result.Start.FilterFormula == nil || *result.Start.FilterFormula != "ISCHANGED(Name)" {
		t.Fatalf("filterFormula: got %v, want ISCHANGED(Name)", result.Start.FilterFormula)
	}
}

// TestCanonicalizeAbsentFieldsStayAbsent verifies that when triggerOrder and
// criteria fields are absent, no phantom fields appear in the output.
func TestCanonicalizeAbsentFieldsStayAbsent(t *testing.T) {
	raw := []byte(`{"start":{"triggerType":"RecordBeforeSave","object":"Account"},"processType":"AutoLaunchedFlow"}`)

	out, err := canonicalizeFlowMetadata(raw)
	if err != nil {
		t.Fatalf("canonicalizeFlowMetadata returned error: %v", err)
	}

	if strings.Contains(string(out), "triggerOrder") {
		t.Fatalf("output contains unexpected triggerOrder field: %s", out)
	}
	if strings.Contains(string(out), "doesRequireRecordChangedToMeetCriteria") {
		t.Fatalf("output contains unexpected doesRequireRecordChangedToMeetCriteria field: %s", out)
	}
}

// TestCanonicalizeMalformedFieldsPreserved verifies that a non-numeric
// triggerOrder does not cause canonicalizeFlowMetadata to fail, and that the
// malformed value is preserved in the output.
func TestCanonicalizeMalformedFieldsPreserved(t *testing.T) {
	raw := []byte(`{"start":{"triggerType":"RecordBeforeSave","object":"Account","triggerOrder":"bad"},"processType":"AutoLaunchedFlow"}`)

	out, err := canonicalizeFlowMetadata(raw)
	if err != nil {
		t.Fatalf("canonicalizeFlowMetadata must not fail on malformed triggerOrder: %v", err)
	}

	var result struct {
		Start struct {
			TriggerOrder any `json:"triggerOrder"`
		} `json:"start"`
	}
	if err := json.Unmarshal(out, &result); err != nil {
		t.Fatalf("failed to unmarshal output: %v", err)
	}
	if result.Start.TriggerOrder == nil {
		t.Fatal("malformed triggerOrder was dropped instead of being preserved")
	}
}

// TestCanonicalizeIdempotent verifies that running canonicalize twice produces
// byte-equal output.
func TestCanonicalizeIdempotent(t *testing.T) {
	raw := []byte(`{"start":{"triggerType":"RecordBeforeSave","object":"Account","triggerOrder":650},"processType":"AutoLaunchedFlow"}`)

	canonical1, err := canonicalizeFlowMetadata(raw)
	if err != nil {
		t.Fatalf("first canonicalize failed: %v", err)
	}
	canonical2, err := canonicalizeFlowMetadata(canonical1)
	if err != nil {
		t.Fatalf("second canonicalize failed: %v", err)
	}
	if !bytes.Equal(canonical1, canonical2) {
		t.Fatalf("canonicalize is not idempotent:\n  first:  %s\n  second: %s", canonical1, canonical2)
	}
}

func TestCanonicalizeRejectsLegacyEmailAlert(t *testing.T) {
	raw := []byte(`{"label":"Legacy Flow","processType":"AutoLaunchedFlow","actionCalls":[{"name":"Legacy_Email_Alert","actionName":"Account.Legacy_Alert","actionType":"emailAlert"}]}`)

	_, err := canonicalizeFlowMetadata(raw)
	if err == nil {
		t.Fatal("canonicalizeFlowMetadata accepted legacy emailAlert actionCall")
	}
	for _, want := range []string{"tenant flow metadata", "Legacy_Email_Alert", "actionType=emailAlert", "sendEmail", "remove it"} {
		if !strings.Contains(err.Error(), want) {
			t.Fatalf("error = %v, want substring %q", err, want)
		}
	}
}
