package integrationservice

import (
	"strings"
	"testing"
	"time"
)

func TestMailboxStateCodecBindsCallbackAuthorityWithoutCookie(t *testing.T) {
	codec, err := newMailboxStateCodec([]byte(strings.Repeat("k", 32)))
	if err != nil {
		t.Fatal(err)
	}
	now := time.Date(2026, 8, 23, 10, 0, 0, 0, time.UTC)
	codec.now = func() time.Time { return now }
	payload := mailboxState{
		OAuthState: "oauth-state", AuthorizedSubjectID: "user-1",
		ReauthorizationHandle: "runtime-handle", ConnectorKey: "connected-mail/generation",
		CallbackURI:  "https://crm.example/api/connected-mailboxes/gmail/callback",
		ScopeDigest:  "sha256:" + strings.Repeat("a", sha256HexLength),
		ReturnTarget: "/lightning/r/Contact/1/view", ExpiresAt: now.Add(time.Minute),
	}
	encoded, err := codec.seal(payload)
	if err != nil {
		t.Fatal(err)
	}
	decoded, err := codec.open(encoded)
	if err != nil {
		t.Fatal(err)
	}
	payload.Version = mailboxStateVersion
	if decoded != payload {
		t.Fatalf("decoded = %#v, want %#v", decoded, payload)
	}

	middle := len(encoded) / 2
	replacement := byte('A')
	if encoded[middle] == replacement {
		replacement = 'B'
	}
	tampered := encoded[:middle] + string(replacement) + encoded[middle+1:]
	if _, err := codec.open(tampered); err == nil {
		t.Fatal("tampered state was accepted")
	}
	foreign, err := newMailboxStateCodec([]byte(strings.Repeat("f", 32)))
	if err != nil {
		t.Fatal(err)
	}
	foreign.now = codec.now
	if _, err := foreign.open(encoded); err == nil {
		t.Fatal("state sealed by a foreign release key was accepted")
	}
}

func TestMailboxStateCodecRejectsExpiredAndUnboundedState(t *testing.T) {
	codec, err := newMailboxStateCodec([]byte(strings.Repeat("k", 32)))
	if err != nil {
		t.Fatal(err)
	}
	now := time.Now().UTC()
	codec.now = func() time.Time { return now }
	valid := mailboxState{
		OAuthState: "oauth-state", AuthorizedSubjectID: "user-1",
		ReauthorizationHandle: "runtime-handle", ConnectorKey: "connected-mail/generation",
		CallbackURI:  "https://crm.example/callback",
		ScopeDigest:  "sha256:" + strings.Repeat("a", sha256HexLength),
		ReturnTarget: "/home", ExpiresAt: now.Add(time.Second),
	}
	encoded, err := codec.seal(valid)
	if err != nil {
		t.Fatal(err)
	}
	codec.now = func() time.Time { return now.Add(2 * time.Second) }
	if _, err := codec.open(encoded); err == nil {
		t.Fatal("expired state was accepted")
	}
	codec.now = func() time.Time { return now }
	valid.ExpiresAt = now.Add(16 * time.Minute)
	if _, err := codec.seal(valid); err == nil {
		t.Fatal("state beyond the callback lifetime bound was accepted")
	}
	valid.ExpiresAt = now.Add(time.Minute)
	valid.ReauthorizationHandle = strings.Repeat("h", 1025)
	if _, err := codec.seal(valid); err == nil {
		t.Fatal("oversized Runtime handle was accepted")
	}
}

func TestMailboxReturnTargetRejectsCrossOriginAndEncodedBackslash(t *testing.T) {
	for _, value := range []string{
		"https://evil.example/path",
		"//evil.example/path",
		"/\\evil.example/path",
		"/%5cevil.example/path",
		"/%2f%2fevil.example/path",
		"/path\r\nLocation: https://evil.example",
	} {
		if validMailboxReturnTarget(value) || safeMailboxReturnTarget(value) != "/" {
			t.Fatalf("unsafe return target %q was accepted", value)
		}
	}
	for _, value := range []string{"/", "/Contact/003", "/search?q=mail#result"} {
		if !validMailboxReturnTarget(value) || safeMailboxReturnTarget(value) != value {
			t.Fatalf("safe return target %q was rejected", value)
		}
	}
}
