package auth

import (
	"encoding/base64"
	"fmt"
	"strings"
	"testing"

	"golang.org/x/crypto/argon2"
)

func TestHashVerifyRoundTrip(t *testing.T) {
	hash, err := HashPassword("correct horse battery staple")
	if err != nil {
		t.Fatal(err)
	}
	if !strings.HasPrefix(hash, "$argon2id$v=19$m=65536,t=3,p=2$") {
		t.Fatalf("hash %q does not embed the spec parameters", hash)
	}

	ok, err := VerifyPassword("correct horse battery staple", hash)
	if err != nil || !ok {
		t.Fatalf("correct password: ok=%v err=%v", ok, err)
	}
	ok, err = VerifyPassword("wrong password", hash)
	if err != nil || ok {
		t.Fatalf("wrong password: ok=%v err=%v", ok, err)
	}
}

func TestHashesAreSalted(t *testing.T) {
	a, _ := HashPassword("same")
	b, _ := HashPassword("same")
	if a == b {
		t.Fatal("two hashes of the same password must differ")
	}
}

func TestVerifyRejectsGarbage(t *testing.T) {
	for _, bad := range []string{
		"",
		"plaintext",
		"$bcrypt$whatever",
		"$argon2id$v=19$m=65536,t=3,p=2$!!!$AAA",
		"$argon2id$v=18$m=65536,t=3,p=2$c2FsdA$AAA",
	} {
		if ok, err := VerifyPassword("x", bad); ok || err == nil {
			t.Errorf("VerifyPassword(%q): ok=%v err=%v, want rejection", bad, ok, err)
		}
	}
}

func TestVerifyHonorsEmbeddedParams(t *testing.T) {
	// A hash created with different (weaker) params must still verify:
	// params come from the hash string, not the current constants.
	salt := []byte("saltsaltsaltsalt")
	key := argon2.IDKey([]byte("pw"), salt, 1, 16*1024, 1, 32)
	legacy := fmt.Sprintf("$argon2id$v=19$m=16384,t=1,p=1$%s$%s",
		base64.RawStdEncoding.EncodeToString(salt),
		base64.RawStdEncoding.EncodeToString(key))
	ok, err := VerifyPassword("pw", legacy)
	if err != nil || !ok {
		t.Fatalf("legacy-param hash: ok=%v err=%v", ok, err)
	}
}

func TestNewTokenShape(t *testing.T) {
	a, b := NewToken(), NewToken()
	if a == b {
		t.Fatal("tokens must be unique")
	}
	if len(a) != 43 { // 32 bytes base64url, unpadded
		t.Fatalf("token length = %d, want 43", len(a))
	}
}
