package auth

import (
	"testing"
)

func TestHashAndCheckPassword(t *testing.T) {
	hash, err := HashPassword("my-password-123")
	if err != nil {
		t.Fatalf("HashPassword failed: %v", err)
	}
	if hash == "my-password-123" {
		t.Fatal("hash equals plaintext")
	}

	// Correct password
	if err := CheckPassword(hash, "my-password-123"); err != nil {
		t.Fatalf("CheckPassword rejected correct password: %v", err)
	}

	// Wrong password
	if err := CheckPassword(hash, "wrong-password"); err == nil {
		t.Fatal("CheckPassword accepted wrong password")
	}
}

func TestGenerateAndValidateToken(t *testing.T) {
	key := [32]byte{}
	copy(key[:], "test-jwt-secret-32-bytes-needed!")
	la := NewLocalAuth(key)

	claims := Claims{UserID: "u1", Username: "admin", Role: "admin"}
	token, err := la.GenerateToken(claims)
	if err != nil {
		t.Fatalf("GenerateToken failed: %v", err)
	}
	if token.Token == "" {
		t.Fatal("generated empty token")
	}

	// Validate the token
	got, err := la.ValidateToken(token.Token)
	if err != nil {
		t.Fatalf("ValidateToken failed: %v", err)
	}
	if got.UserID != "u1" || got.Username != "admin" || got.Role != "admin" {
		t.Fatalf("claims mismatch: %+v", got)
	}
}

func TestValidateInvalidToken(t *testing.T) {
	key := [32]byte{}
	copy(key[:], "test-jwt-secret-32-bytes-needed!")
	la := NewLocalAuth(key)

	_, err := la.ValidateToken("invalid-token-string")
	if err == nil {
		t.Fatal("ValidateToken should reject invalid token")
	}
}

func TestValidateTokenFromDifferentKey(t *testing.T) {
	key1 := [32]byte{}
	copy(key1[:], "first-jwt-secret-32-bytes-needed")
	la1 := NewLocalAuth(key1)

	key2 := [32]byte{}
	copy(key2[:], "other-jwt-secret-32-bytes-needed")
	la2 := NewLocalAuth(key2)

	claims := Claims{UserID: "u1", Username: "admin", Role: "admin"}
	token, err := la1.GenerateToken(claims)
	if err != nil {
		t.Fatalf("GenerateToken failed: %v", err)
	}

	// Token signed with key1 should not validate with key2
	_, err = la2.ValidateToken(token.Token)
	if err == nil {
		t.Fatal("ValidateToken should reject token signed with different key")
	}
}

func TestHashPasswordDifferentOutputs(t *testing.T) {
	hash1, err := HashPassword("same-password")
	if err != nil {
		t.Fatalf("HashPassword 1 failed: %v", err)
	}

	hash2, err := HashPassword("same-password")
	if err != nil {
		t.Fatalf("HashPassword 2 failed: %v", err)
	}

	if hash1 == hash2 {
		t.Fatal("bcrypt should produce different hashes for same input (random salt)")
	}

	// Both should still verify
	if err := CheckPassword(hash1, "same-password"); err != nil {
		t.Fatal("hash1 should verify")
	}
	if err := CheckPassword(hash2, "same-password"); err != nil {
		t.Fatal("hash2 should verify")
	}
}

func TestTokenContainsClaims(t *testing.T) {
	key := [32]byte{}
	copy(key[:], "test-jwt-secret-32-bytes-needed!")
	la := NewLocalAuth(key)

	claims := Claims{UserID: "user-42", Username: "testuser", Role: "viewer"}
	token, err := la.GenerateToken(claims)
	if err != nil {
		t.Fatalf("GenerateToken failed: %v", err)
	}

	got, err := la.ValidateToken(token.Token)
	if err != nil {
		t.Fatalf("ValidateToken failed: %v", err)
	}
	if got.UserID != "user-42" {
		t.Fatalf("UserID mismatch: got %q, want %q", got.UserID, "user-42")
	}
	if got.Username != "testuser" {
		t.Fatalf("Username mismatch: got %q, want %q", got.Username, "testuser")
	}
	if got.Role != "viewer" {
		t.Fatalf("Role mismatch: got %q, want %q", got.Role, "viewer")
	}
}
