package ldapauth

import (
	"errors"
	"os"
	"path/filepath"
	"testing"
)

func TestNewRequiresURIAndBase(t *testing.T) {
	if _, err := New(Config{SearchBase: "dc=x"}); err == nil {
		t.Fatal("want error for missing uri")
	}
	if _, err := New(Config{URIs: []string{"ldap://x"}}); err == nil {
		t.Fatal("want error for missing search_base")
	}
}

func TestNewAppliesSSSDDefaults(t *testing.T) {
	a, err := New(Config{URIs: []string{"ldap://x"}, SearchBase: "dc=x"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if a.cfg.UserAttr != "uid" {
		t.Errorf("UserAttr default = %q, want uid", a.cfg.UserAttr)
	}
	if a.cfg.UserObjectClass != "posixAccount" {
		t.Errorf("UserObjectClass default = %q, want posixAccount", a.cfg.UserObjectClass)
	}
	if a.cfg.Timeout <= 0 {
		t.Errorf("Timeout default not applied: %v", a.cfg.Timeout)
	}
}

// Empty passwords must be rejected before any bind: a non-empty DN + empty
// password is an "unauthenticated bind" many servers accept as success.
func TestAuthenticateRejectsEmptyPasswordWithoutDialing(t *testing.T) {
	// An unreachable URI guarantees the test fails loudly if it ever tries to dial.
	a, err := New(Config{URIs: []string{"ldap://127.0.0.1:1"}, SearchBase: "dc=x"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if err := a.Authenticate("alice", ""); !errors.Is(err, ErrInvalidCredentials) {
		t.Fatalf("want ErrInvalidCredentials for empty password, got %v", err)
	}
}

func TestNewRejectsBadCACert(t *testing.T) {
	dir := t.TempDir()
	bad := filepath.Join(dir, "ca.pem")
	if err := os.WriteFile(bad, []byte("not a certificate"), 0o600); err != nil {
		t.Fatal(err)
	}
	if _, err := New(Config{URIs: []string{"ldaps://x"}, SearchBase: "dc=x", CACertFile: bad}); err == nil {
		t.Fatal("want error for CA file with no certificates")
	}
	if _, err := New(Config{URIs: []string{"ldaps://x"}, SearchBase: "dc=x", CACertFile: filepath.Join(dir, "missing.pem")}); err == nil {
		t.Fatal("want error for missing CA file")
	}
}
