package dialectsfcrm

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

	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
)

func TestDecodeApprovalProcessManagedRequestAcceptsCanonicalSources(t *testing.T) {
	tests := []struct {
		name   string
		mutate func(*cp.ComponentManagedRequest)
	}{
		{name: "inline payload"},
		{
			name: "path-backed payload",
			mutate: func(request *cp.ComponentManagedRequest) {
				request.Component.Path = "approvalProcesses/Quote.Quote_Approval.approvalProcess-meta.xml"
				request.Component.Payload = nil
			},
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			request := approvalProcessManagedRequestForTest(t)
			if test.mutate != nil {
				test.mutate(&request)
			}
			plan, err := decodeApprovalProcessManagedRequest(request)
			if err != nil {
				t.Fatal(err)
			}
			if plan.Parent.BusinessKey != (ApprovalProcessBusinessKey{ObjectAPIName: "Quote", DeveloperName: "Quote_Approval"}) || plan.Parent.OwnerPack != "sales" {
				t.Fatalf("parent identity = (%#v, %q)", plan.Parent.BusinessKey, plan.Parent.OwnerPack)
			}
			if plan.ParentDisposition != ApprovalProcessParentManagedUpsert || plan.PreAppliedParentID != "" {
				t.Fatalf("parent target = (%q,%q), want managed", plan.ParentDisposition, plan.PreAppliedParentID)
			}
			if plan.Parent.RawMetadata != string(request.Payload) {
				t.Fatal("raw metadata was not preserved byte-for-byte")
			}
		})
	}
}

func TestDecodeApprovalProcessManagedRequestRejectsUntrustedInput(t *testing.T) {
	tests := []struct {
		name   string
		mutate func(*cp.ComponentManagedRequest)
		want   string
	}{
		{name: "pack name", mutate: func(r *cp.ComponentManagedRequest) { r.PackName = " sales" }, want: "not canonical"},
		{name: "source owner", mutate: func(r *cp.ComponentManagedRequest) { r.SourceOwner = "tenant" }, want: "must not declare source owner"},
		{name: "kind", mutate: func(r *cp.ComponentManagedRequest) { r.Component.Kind = cp.ComponentKind("flow") }, want: "component kind"},
		{name: "manageability", mutate: func(r *cp.ComponentManagedRequest) { r.Component.Manageability = cp.Manageability("unlocked") }, want: "manageability must be"},
		{name: "business key", mutate: func(r *cp.ComponentManagedRequest) { r.Component.BusinessKey = " Quote/Quote_Approval" }, want: "must be canonical"},
		{name: "missing lifecycle payload", mutate: func(r *cp.ComponentManagedRequest) { r.Payload = nil }, want: "lifecycle payload is required"},
		{name: "conflicting inline payload", mutate: func(r *cp.ComponentManagedRequest) { r.Component.Payload = []byte(`{}`) }, want: "must exactly match"},
		{name: "projection", mutate: func(r *cp.ComponentManagedRequest) { r.ExpectedProjection = []byte(`{}`) }, want: "does not match"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			request := approvalProcessManagedRequestForTest(t)
			test.mutate(&request)
			if _, err := decodeApprovalProcessManagedRequest(request); err == nil || !strings.Contains(err.Error(), test.want) {
				t.Fatalf("error = %v, want containing %q", err, test.want)
			}
		})
	}
}

func approvalProcessManagedRequestForTest(t *testing.T) cp.ComponentManagedRequest {
	t.Helper()
	raw := []byte(" \n<ApprovalProcess><label>Quote approval</label><active>true</active>" +
		"<approvalStep><name>Review</name><label>Review</label><stepApprovalActions><actions><name>Clear Note</name><type>FieldUpdate</type><fieldApiName>Note</fieldApiName><fieldValue>Literal: </fieldValue></actions></stepApprovalActions></approvalStep>" +
		"<initialSubmissionActions><actions><name>Notify Flow</name><type>Flow</type><apiName>Approval_Notify</apiName></actions></initialSubmissionActions>" +
		"</ApprovalProcess>\n")
	return approvalProcessManagedRequestFromRawForTest(t, raw)
}

func approvalProcessManagedRequestFromRawForTest(t *testing.T, raw []byte) cp.ComponentManagedRequest {
	t.Helper()
	metadata, err := DecodeApprovalProcessMetadata(string(raw))
	if err != nil {
		t.Fatal(err)
	}
	plan, err := PlanApprovalProcessPersistence(ApprovalProcessProjectionRequest{
		ObjectAPIName: "Quote", DeveloperName: "Quote_Approval", ParentDisposition: ApprovalProcessParentManagedUpsert,
		OwnerPack: "sales", RawMetadata: string(raw), Metadata: metadata,
	})
	if err != nil {
		t.Fatal(err)
	}
	expected, err := json.Marshal(plan)
	if err != nil {
		t.Fatal(err)
	}
	return cp.ComponentManagedRequest{
		PackName: "sales",
		Component: cp.ManifestComponent{
			Kind: cp.ComponentKindApprovalProcess, BusinessKey: "Quote/Quote_Approval", Manageability: cp.ManageabilityLocked, Payload: append([]byte(nil), raw...),
		},
		Payload: append([]byte(nil), raw...), ExpectedProjection: expected,
	}
}
