package persistence

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

	applicationresultv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/applicationresultv1"
)

func TestRuntimePlatformCommandApplicationCompletionCodecRoundTripsCanonicalEnvelope(t *testing.T) {
	tests := []struct {
		name        string
		completion  RuntimePlatformCommandApplicationCompletion
		wantEncoded string
	}{
		{
			name: "applied object",
			completion: RuntimePlatformCommandApplicationCompletion{
				Outcome: applicationresultv1.OutcomeApplied,
				Body:    json.RawMessage(`{"a":1,"z":2}`),
			},
			wantEncoded: `{"body":{"a":1,"z":2},"failureCode":"","outcome":"APPLIED","version":3}`,
		},
		{
			name: "noop array",
			completion: RuntimePlatformCommandApplicationCompletion{
				Outcome: applicationresultv1.OutcomeNoop,
				Body:    json.RawMessage(`[]`),
			},
			wantEncoded: `{"body":[],"failureCode":"","outcome":"NOOP","version":3}`,
		},
		{
			name: "rejected",
			completion: RuntimePlatformCommandApplicationCompletion{
				Outcome:     applicationresultv1.OutcomeRejected,
				FailureCode: applicationresultv1.FailureApplicationRejected,
				Body:        json.RawMessage(`{"rejected":true}`),
			},
			wantEncoded: `{"body":{"rejected":true},"failureCode":"APPLICATION_REJECTED","outcome":"REJECTED","version":3}`,
		},
		{
			name: "failed",
			completion: RuntimePlatformCommandApplicationCompletion{
				Outcome:     applicationresultv1.OutcomeFailed,
				FailureCode: applicationresultv1.FailureRuntimeExecutionFailed,
				Body:        json.RawMessage(`{}`),
			},
			wantEncoded: `{"body":{},"failureCode":"RUNTIME_EXECUTION_FAILED","outcome":"FAILED","version":3}`,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			encoded, err := encodeRuntimePlatformCommandApplicationCompletion(test.completion)
			if err != nil {
				t.Fatalf("encodeRuntimePlatformCommandApplicationCompletion() error = %v", err)
			}
			if string(encoded) != test.wantEncoded {
				t.Fatalf("encoded completion = %s, want %s", encoded, test.wantEncoded)
			}
			if len(encoded) > applicationresultv1.MaxResultPayloadBytes {
				t.Fatalf("encoded completion bytes = %d, maximum = %d", len(encoded), applicationresultv1.MaxResultPayloadBytes)
			}
			decoded, err := decodeRuntimePlatformCommandApplicationCompletion(encoded)
			if err != nil {
				t.Fatalf("decodeRuntimePlatformCommandApplicationCompletion() error = %v", err)
			}
			if decoded.Outcome != test.completion.Outcome || decoded.FailureCode != test.completion.FailureCode ||
				!bytes.Equal(decoded.Body, test.completion.Body) {
				t.Fatalf("decoded completion = %#v, want %#v", decoded, test.completion)
			}
		})
	}
}

func TestRuntimePlatformCommandApplicationCompletionEncodeRejectsInvalidContent(t *testing.T) {
	valid := RuntimePlatformCommandApplicationCompletion{
		Outcome: applicationresultv1.OutcomeApplied,
		Body:    json.RawMessage(`{"ok":true}`),
	}
	tests := map[string]RuntimePlatformCommandApplicationCompletion{
		"missing body":             {Outcome: applicationresultv1.OutcomeApplied},
		"scalar body":              {Outcome: applicationresultv1.OutcomeApplied, Body: json.RawMessage(`1`)},
		"noncanonical body":        {Outcome: applicationresultv1.OutcomeApplied, Body: json.RawMessage(`{ "ok": true }`)},
		"duplicate body member":    {Outcome: applicationresultv1.OutcomeApplied, Body: json.RawMessage(`{"ok":false,"ok":true}`)},
		"unknown outcome":          {Outcome: applicationresultv1.Outcome("UNKNOWN"), Body: valid.Body},
		"applied with failure":     {Outcome: applicationresultv1.OutcomeApplied, FailureCode: applicationresultv1.FailureApplicationRejected, Body: valid.Body},
		"rejected without failure": {Outcome: applicationresultv1.OutcomeRejected, Body: valid.Body},
		"failed with rejection": {
			Outcome: applicationresultv1.OutcomeFailed, FailureCode: applicationresultv1.FailureApplicationRejected,
			Body: valid.Body,
		},
		"oversized final envelope": {
			Outcome: applicationresultv1.OutcomeApplied,
			Body: json.RawMessage(`{"value":"` +
				strings.Repeat("a", applicationresultv1.MaxResultPayloadBytes) + `"}`),
		},
	}
	for name, completion := range tests {
		t.Run(name, func(t *testing.T) {
			if encoded, err := encodeRuntimePlatformCommandApplicationCompletion(completion); err == nil || encoded != nil {
				t.Fatalf("encodeRuntimePlatformCommandApplicationCompletion() = %q, %v, want rejection", encoded, err)
			}
		})
	}
}

func TestRuntimePlatformCommandApplicationCompletionDecodeRejectsNoncanonicalEnvelope(t *testing.T) {
	valid := `{"body":{"ok":true},"failureCode":"","outcome":"APPLIED","version":3}`
	tests := map[string]string{
		"empty":                 "",
		"unknown field":         `{"body":{"ok":true},"extra":true,"failureCode":"","outcome":"APPLIED","version":3}`,
		"duplicate field":       `{"body":{"ok":true},"body":{},"failureCode":"","outcome":"APPLIED","version":3}`,
		"missing version":       `{"body":{"ok":true},"failureCode":"","outcome":"APPLIED"}`,
		"missing outcome":       `{"body":{"ok":true},"failureCode":"","version":3}`,
		"missing failure code":  `{"body":{"ok":true},"outcome":"APPLIED","version":3}`,
		"missing body":          `{"failureCode":"","outcome":"APPLIED","version":3}`,
		"trailing value":        valid + `{}`,
		"trailing whitespace":   valid + " ",
		"wrong key order":       `{"version":3,"outcome":"APPLIED","failureCode":"","body":{"ok":true}}`,
		"noncanonical body":     `{"body":{"z":1,"a":2},"failureCode":"","outcome":"APPLIED","version":3}`,
		"duplicate body member": `{"body":{"ok":false,"ok":true},"failureCode":"","outcome":"APPLIED","version":3}`,
		"scalar body":           `{"body":1,"failureCode":"","outcome":"APPLIED","version":3}`,
		"wrong version":         `{"body":{"ok":true},"failureCode":"","outcome":"APPLIED","version":2}`,
		"null failure code":     `{"body":{"ok":true},"failureCode":null,"outcome":"APPLIED","version":3}`,
		"invalid pair":          `{"body":{"ok":true},"failureCode":"APPLICATION_REJECTED","outcome":"APPLIED","version":3}`,
		"unknown outcome":       `{"body":{"ok":true},"failureCode":"","outcome":"UNKNOWN","version":3}`,
	}
	for name, raw := range tests {
		t.Run(name, func(t *testing.T) {
			if decoded, err := decodeRuntimePlatformCommandApplicationCompletion([]byte(raw)); err == nil ||
				decoded.Outcome != "" || decoded.FailureCode != "" || decoded.Body != nil {
				t.Fatalf("decodeRuntimePlatformCommandApplicationCompletion() = %#v, %v, want rejection", decoded, err)
			}
		})
	}

	oversized := bytes.Repeat([]byte{' '}, applicationresultv1.MaxResultPayloadBytes+1)
	if decoded, err := decodeRuntimePlatformCommandApplicationCompletion(oversized); err == nil ||
		decoded.Outcome != "" || decoded.FailureCode != "" || decoded.Body != nil {
		t.Fatalf("oversized decode = %#v, %v, want rejection", decoded, err)
	}
}
