package secretbox

import (
	"encoding/base64"
	"errors"
	"strings"
	"testing"
)

func testCodec(t *testing.T) *Codec {
	t.Helper()
	c, err := New("test-key", base64.StdEncoding.EncodeToString(make([]byte, 32)))
	if err != nil {
		t.Fatal(err)
	}
	return c
}

func TestCodecRoundTripUsesRandomNonce(t *testing.T) {
	c := testCodec(t)
	one, err := c.Encrypt(42, "api_key", "xai", "top-secret")
	if err != nil {
		t.Fatal(err)
	}
	two, err := c.Encrypt(42, "api_key", "xai", "top-secret")
	if err != nil {
		t.Fatal(err)
	}
	if one == two {
		t.Fatal("ciphertexts must differ")
	}
	if !strings.HasPrefix(one, "enc:v1:test-key:") {
		t.Fatalf("unexpected prefix: %q", one)
	}
	got, err := c.Decrypt(42, "api_key", "xai", one)
	if err != nil || got != "top-secret" {
		t.Fatalf("round trip = %q, %v", got, err)
	}
}

func TestCodecAADAndTamperingFail(t *testing.T) {
	c := testCodec(t)
	value, err := c.Encrypt(42, "api_key", "xai", "top-secret")
	if err != nil {
		t.Fatal(err)
	}
	parts := strings.Split(value, ":")
	payload, err := base64.RawURLEncoding.DecodeString(parts[3])
	if err != nil {
		t.Fatal(err)
	}
	payload[len(payload)-1] ^= 0x01
	tampered := strings.Join([]string{parts[0], parts[1], parts[2], base64.RawURLEncoding.EncodeToString(payload)}, ":")
	for _, tc := range []struct {
		name     string
		uid      int64
		field    string
		provider string
		value    string
	}{
		{name: "uid", uid: 43, field: "api_key", provider: "xai", value: value},
		{name: "field", uid: 42, field: "exa_api_key", provider: "xai", value: value},
		{name: "provider", uid: 42, field: "api_key", provider: "other", value: value},
		{name: "tamper", uid: 42, field: "api_key", provider: "xai", value: tampered},
	} {
		t.Run(tc.name, func(t *testing.T) {
			if _, err := c.Decrypt(tc.uid, tc.field, tc.provider, tc.value); !errors.Is(err, ErrInvalidCiphertext) {
				t.Fatalf("error = %v", err)
			}
		})
	}
}

func TestCodecCompatibilityAndValidation(t *testing.T) {
	c := testCodec(t)
	got, err := c.Decrypt(1, "api_key", "xai", "legacy-plaintext")
	if err != nil || got != "legacy-plaintext" {
		t.Fatalf("legacy = %q, %v", got, err)
	}
	if _, err := c.Decrypt(1, "api_key", "xai", "enc:v2:key:data"); !errors.Is(err, ErrUnsupportedVersion) {
		t.Fatalf("version error = %v", err)
	}
	if _, err := c.Decrypt(1, "api_key", "xai", "enc:v1:other:data"); !errors.Is(err, ErrInvalidCiphertext) {
		t.Fatalf("key ID error = %v", err)
	}
	if _, err := New("key", base64.StdEncoding.EncodeToString(make([]byte, 31))); !errors.Is(err, ErrInvalidConfig) {
		t.Fatalf("key size error = %v", err)
	}
}
