package password

import (
	"testing"

	"github.com/atlasos/calypso/internal/common/config"
)

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func contains(s, substr string) bool {
	for i := 0; i <= len(s)-len(substr); i++ {
		if s[i:i+len(substr)] == substr {
			return true
		}
	}
	return false
}

func TestHashPassword(t *testing.T) {
	params := config.Argon2Params{
		Memory:      64 * 1024,
		Iterations:  3,
		Parallelism: 4,
		SaltLength:  16,
		KeyLength:   32,
	}

	password := "test-password-123"
	hash, err := HashPassword(password, params)
	if err != nil {
		t.Fatalf("HashPassword failed: %v", err)
	}

	// Verify hash format
	if hash == "" {
		t.Error("HashPassword returned empty string")
	}

	// Verify hash starts with Argon2id prefix
	if len(hash) < 12 || hash[:12] != "$argon2id$v=" {
		t.Errorf("Hash does not start with expected prefix, got: %s", hash[:min(30, len(hash))])
	}

	// Verify hash contains required components
	if !contains(hash, "$m=") || !contains(hash, ",t=") || !contains(hash, ",p=") {
		t.Errorf("Hash missing required components, got: %s", hash[:min(50, len(hash))])
	}

	// Verify hash is different each time (due to random salt)
	hash2, err := HashPassword(password, params)
	if err != nil {
		t.Fatalf("HashPassword failed on second call: %v", err)
	}

	if hash == hash2 {
		t.Error("HashPassword returned same hash for same password (salt should be random)")
	}
}

func TestVerifyPassword(t *testing.T) {
	params := config.Argon2Params{
		Memory:      64 * 1024,
		Iterations:  3,
		Parallelism: 4,
		SaltLength:  16,
		KeyLength:   32,
	}

	password := "test-password-123"
	hash, err := HashPassword(password, params)
	if err != nil {
		t.Fatalf("HashPassword failed: %v", err)
	}

	// Test correct password
	valid, err := VerifyPassword(password, hash)
	if err != nil {
		t.Fatalf("VerifyPassword failed: %v", err)
	}
	if !valid {
		t.Error("VerifyPassword returned false for correct password")
	}

	// Test wrong password
	valid, err = VerifyPassword("wrong-password", hash)
	if err != nil {
		t.Fatalf("VerifyPassword failed: %v", err)
	}
	if valid {
		t.Error("VerifyPassword returned true for wrong password")
	}

	// Test empty password
	valid, err = VerifyPassword("", hash)
	if err != nil {
		t.Fatalf("VerifyPassword failed: %v", err)
	}
	if valid {
		t.Error("VerifyPassword returned true for empty password")
	}
}

func TestVerifyPassword_InvalidHash(t *testing.T) {
	tests := []struct {
		name string
		hash string
	}{
		{"empty hash", ""},
		{"invalid format", "not-a-hash"},
		{"wrong algorithm", "$argon2$v=19$m=65536,t=3,p=4$salt$hash"},
		{"incomplete hash", "$argon2id$v=19"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			valid, err := VerifyPassword("test-password", tt.hash)
			if err == nil {
				t.Error("VerifyPassword should return error for invalid hash")
			}
			if valid {
				t.Error("VerifyPassword should return false for invalid hash")
			}
		})
	}
}

func TestHashPassword_DifferentPasswords(t *testing.T) {
	params := config.Argon2Params{
		Memory:      64 * 1024,
		Iterations:  3,
		Parallelism: 4,
		SaltLength:  16,
		KeyLength:   32,
	}

	password1 := "password1"
	password2 := "password2"

	hash1, err := HashPassword(password1, params)
	if err != nil {
		t.Fatalf("HashPassword failed: %v", err)
	}

	hash2, err := HashPassword(password2, params)
	if err != nil {
		t.Fatalf("HashPassword failed: %v", err)
	}

	// Hashes should be different
	if hash1 == hash2 {
		t.Error("Different passwords produced same hash")
	}

	// Each password should verify against its own hash
	valid, err := VerifyPassword(password1, hash1)
	if err != nil || !valid {
		t.Error("Password1 should verify against its own hash")
	}

	valid, err = VerifyPassword(password2, hash2)
	if err != nil || !valid {
		t.Error("Password2 should verify against its own hash")
	}

	// Passwords should not verify against each other's hash
	valid, err = VerifyPassword(password1, hash2)
	if err != nil || valid {
		t.Error("Password1 should not verify against password2's hash")
	}

	valid, err = VerifyPassword(password2, hash1)
	if err != nil || valid {
		t.Error("Password2 should not verify against password1's hash")
	}
}

