<?php

// Strenge Typprüfung für Parameter- und Rückgabetypen aktivieren.
declare(strict_types=1);

namespace App\Services\Ldap;

use RuntimeException;

/**
 * Service zur Authentifizierung von Benutzern gegen ein Active Directory per LDAP/LDAPS.
 * Bekommt Benutzername und Passwort und liefert:
 * - true  -> Anmeldedaten sind gültig
 * - false -> Anmeldedaten sind fachlich ungültig (z. B. falsches Passwort)
 * Technische Fehler (z. B. falsche Konfiguration, keine Verbindung) werden als Exception geworfen.
 */
class LdapAuthService
{
    /** @var array<string, mixed> LDAP-spezifische Konfiguration (Server, Port, Domain-Suffix, Timeout, etc.) */
    private array $config;

    private LdapConnectionHelper $connectionHelper;

    /**
     * Erwartet den Teilbereich "ldap" aus der allgemeinen Konfiguration (config.php).
     *
     * @param array<string, mixed> $ldapConfig Konfiguration für die LDAP-Verbindung
     */
    public function __construct(array $ldapConfig)
    {
        // LDAP-Konfiguration in der Instanz speichern.
        $this->config = $ldapConfig;
        $this->connectionHelper = new LdapConnectionHelper($ldapConfig);
    }

    /**
     * Führt die eigentliche LDAP/AD-Authentifizierung durch.
     *
     * @param string $username Benutzername (ohne Domain-Suffix, z. B. "administrator")
     * @param string $password Passwort im Klartext (wird direkt an ldap_bind übergeben)
     *
     * @return bool true bei erfolgreicher Anmeldung, false bei fachlich ungültigen Anmeldedaten
     *
     * @throws RuntimeException bei technischen Problemen (z. B. fehlende Konfiguration, Verbindungsfehler)
     */
    public function authenticate(string $username, string $password): bool
    {
        // Wenn Benutzername oder Passwort leer sind, gar nicht erst versuchen zu binden.
        // Das ist ein fachlich ungültiger Login und wird mit false behandelt.
        if ($username === '' || $password === '') {
            return false;
        }

        // Benötigte Werte aus der LDAP-Konfiguration auslesen.
        // Falls einzelne Werte fehlen, werden sinnvolle Standardwerte gesetzt.
        $domainSuffix = (string)($this->config['domain_suffix'] ?? '');

        // Ohne Server-Adresse oder Domain-Suffix ist eine sinnvolle Anmeldung nicht möglich.
        // Das ist ein Konfigurationsfehler und wird als technischer Fehler behandelt.
        if ($domainSuffix === '') {
            throw new RuntimeException('LDAP-Konfiguration ist unvollständig (domain_suffix fehlt).');
        }

        // Verbindung zum LDAP/AD-Server herstellen.
        // Rückgabewert ist eine Ressource (Verbindungshandle) oder false bei Fehlern.
        $connection = $this->connectionHelper->createConnection();

        // UPN (User Principal Name) aus Benutzername und Domain-Suffix bilden.
        // Beispiel: "administrator" + "@ITFA-PROJ-DOM.local" -> "administrator@ITFA-PROJ-DOM.local"
        $bindRdn = $username . $domainSuffix;

        // Eigentliche Authentifizierung:
        // ldap_bind versucht, sich mit den angegebenen Anmeldedaten am AD/LDAP anzumelden.
        // Rückgabewerte:
        // - true  -> Anmeldedaten sind gültig
        // - false -> Anmeldedaten sind ungültig (z. B. falsches Passwort)
        //
        // Das @-Zeichen unterdrückt PHP-Warnings, damit Fehlermeldungen nicht unformatiert im HTML landen.
        $bindResult = @ldap_bind($connection, $bindRdn, $password);

        // Verbindung zum LDAP-Server wieder schließen.
        ldap_unbind($connection);

        // Bei erfolgreichem Bind (true) -> Authentifizierung erfolgreich.
        // Bei false -> fachlich fehlgeschlagene Anmeldung (z. B. falsche Credentials).
        return $bindResult === true;
    }
}
