package outboundintentv1

import (
	"crypto/sha256"
	"encoding/hex"
	"testing"
)

func TestDecodeMailDeliveryV1AcceptsCanonicalProviderNeutralPayload(t *testing.T) {
	raw := []byte(`{"version":1,"authorizedActorId":"user-1","toAddresses":["recipient@example.com"],"subject":"Hello","textBody":"Plain text body"}`)
	intent := testIntent(raw, 41)
	intent.Operation = OperationSendMail
	intent.PayloadFormatVersion = MailPayloadFormatV1

	payload, err := DecodeMailDeliveryV1(intent)
	if err != nil {
		t.Fatalf("DecodeMailDeliveryV1: %v", err)
	}
	if payload.AuthorizedActorID != "user-1" || len(payload.ToAddresses) != 1 ||
		payload.Subject != "Hello" || payload.TextBody != "Plain text body" {
		t.Fatalf("payload = %#v", payload)
	}
}

func TestDecodeMailDeliveryV1FailsClosed(t *testing.T) {
	tests := map[string]string{
		"provider selected by Runtime": `{"version":1,"authorizedActorId":"user-1","toAddresses":["recipient@example.com"],"subject":"Hello","textBody":"Body","provider":"gmail"}`,
		"caller supplied sender":       `{"version":1,"authorizedActorId":"user-1","toAddresses":["recipient@example.com"],"subject":"Hello","textBody":"Body","fromAddress":"sender@example.com"}`,
		"html body":                    `{"version":1,"authorizedActorId":"user-1","toAddresses":["recipient@example.com"],"subject":"Hello","textBody":"Body","htmlBody":"<b>Body</b>"}`,
		"missing actor":                `{"version":1,"toAddresses":["recipient@example.com"],"subject":"Hello","textBody":"Body"}`,
		"duplicate recipient":          `{"version":1,"authorizedActorId":"user-1","toAddresses":["recipient@example.com","recipient@example.com"],"subject":"Hello","textBody":"Body"}`,
		"control in subject":           "{\"version\":1,\"authorizedActorId\":\"user-1\",\"toAddresses\":[\"recipient@example.com\"],\"subject\":\"Hello\\u0000\",\"textBody\":\"Body\"}",
		"noncanonical":                 `{ "version":1,"authorizedActorId":"user-1","toAddresses":["recipient@example.com"],"subject":"Hello","textBody":"Body"}`,
	}
	for name, raw := range tests {
		t.Run(name, func(t *testing.T) {
			intent := testIntent([]byte(raw), 41)
			intent.Operation = OperationSendMail
			intent.PayloadFormatVersion = MailPayloadFormatV1
			if _, err := DecodeMailDeliveryV1(intent); err == nil {
				t.Fatal("malformed mail payload was accepted")
			}
		})
	}
}

func TestMailDeliveryResultV1IsBoundedAndRedacted(t *testing.T) {
	delivered := []byte(`{"version":1,"outcome":"DELIVERED","remoteMessageId":"remote-1","senderAddress":"sender@example.com"}`)
	result, err := DecodeMailDeliveryResultV1(delivered)
	if err != nil {
		t.Fatalf("DecodeMailDeliveryResultV1(delivered): %v", err)
	}
	if result.Outcome != MailOutcomeDelivered || result.RemoteMessageID != "remote-1" {
		t.Fatalf("result = %#v", result)
	}

	rejected := []byte(`{"version":1,"outcome":"REJECTED","failureCode":"MAILBOX_NOT_CONNECTED"}`)
	if _, err := DecodeMailDeliveryResultV1(rejected); err != nil {
		t.Fatalf("DecodeMailDeliveryResultV1(rejected): %v", err)
	}

	unknown := []byte(`{"version":1,"outcome":"OUTCOME_UNKNOWN","failureCode":"PROVIDER_OUTCOME_UNKNOWN"}`)
	if _, err := DecodeMailDeliveryResultV1(unknown); err != nil {
		t.Fatalf("DecodeMailDeliveryResultV1(unknown): %v", err)
	}

	for name, raw := range map[string]string{
		"provider response":  `{"version":1,"outcome":"REJECTED","failureCode":"PROVIDER_REJECTED","providerResponse":"secret"}`,
		"raw failure":        `{"version":1,"outcome":"REJECTED","failureCode":"token expired: abc"}`,
		"missing receipt":    `{"version":1,"outcome":"DELIVERED","senderAddress":"sender@example.com"}`,
		"receipt on failure": `{"version":1,"outcome":"REJECTED","remoteMessageId":"remote-1","failureCode":"PROVIDER_REJECTED"}`,
	} {
		t.Run(name, func(t *testing.T) {
			if _, err := DecodeMailDeliveryResultV1([]byte(raw)); err == nil {
				t.Fatal("unsafe mail result was accepted")
			}
		})
	}
}

func TestCommitMailResultRequiresExactTypedResult(t *testing.T) {
	payload := []byte(`{"version":1,"authorizedActorId":"user-1","toAddresses":["recipient@example.com"],"subject":"Hello","textBody":"Body"}`)
	intent := testIntent(payload, 41)
	intent.Operation = OperationSendMail
	intent.PayloadFormatVersion = MailPayloadFormatV1
	result := []byte(`{"version":1,"outcome":"DELIVERED","remoteMessageId":"remote-1","senderAddress":"sender@example.com"}`)
	sum := sha256.Sum256(result)
	request := CommitResultRequest{
		IntentID: intent.IntentID, PayloadDigest: intent.PayloadDigest,
		CatalogRevision: intent.CatalogRevision, IntegrationCommandID: "command-1",
		ResultFormatVersion: MailResultFormatV1,
		ResultDigest:        "sha256:" + hex.EncodeToString(sum[:]),
		CanonicalResult:     result,
	}
	if err := ValidateCommitResultRequest(ctxWithDeadline(t), request); err != nil {
		t.Fatalf("ValidateCommitResultRequest: %v", err)
	}
	if err := ValidateCommitResultRequestForIntent(intent, request); err != nil {
		t.Fatalf("ValidateCommitResultRequestForIntent: %v", err)
	}

	legacy := request
	legacy.ResultFormatVersion = 0
	legacy.ResultDigest = ""
	legacy.CanonicalResult = nil
	legacy.RemoteID = "remote-1"
	if err := ValidateCommitResultRequestForIntent(intent, legacy); err == nil {
		t.Fatal("mail intent accepted the legacy untyped remote-id result")
	}
}
