<?php
/**
 * AuthController (auth/AuthController.php)
 * --------------------------------------------------
 * Zuständig für:
 *   • Benutzer‑Authentifizierung (Login / Logout)
 *   • Passwort‑Reset‑Workflow (Token erstellen, Mail versenden, Passwort setzen)
 *   • Sitzungsverwaltung (isLoggedIn)
 *
 * Gemäß den Mailresponder v9‑Vorgaben werden hier umgesetzt:
 *   • Ausführliche, leicht verständliche Kommentare in allen kritischen Blöcken
 *   • Fehlerbehandlung via try/catch bei *jeder* Datenbank‑Operation
 *   • Vollständiges SQL‑Logging mit ErrorLogger::logSqlSuccess bzw. logSqlError
 *   • User‑Aktions‑Logging via logUserAction()
 *   • Optionale Settings‑Änderungen könnten über logSettingChange() festgehalten werden
 *
 * Hinweis ✔️  Die Hilfsfunktionen logUserAction() und logSettingChange() sind global in
 *            includes/functions.php definiert – hier NICHT erneut deklarieren!
 */

// --------------------------------------------------
// 1) Abhängigkeiten laden
// --------------------------------------------------
require_once __DIR__ . '/../includes/init.php';    // Initialisiert Autoloading, DB‑Singleton etc.
require_once __DIR__ . '/../includes/core/Mailer.php'; // E‑Mail‑Versand
require_once __DIR__ . '/../includes/functions.php';  // enthält logUserAction() / logSettingChange()

// --------------------------------------------------
// 2) Controller‑Klasse definieren
// --------------------------------------------------
class AuthController
{
    /** @var PDO  */
    private $db;
    /** @var array Konfigurationsarray */
    private $config;

    // --------------------------------------------------
    // 2.1) Konstruktor – lädt DB‑Instanz & Konfiguration
    // --------------------------------------------------
    public function __construct()
    {
        $this->db     = Database::getInstance();                  // Singleton‑PDO
        $this->config = include __DIR__ . '/../includes/config.php'; // App‑Konfig
    }

    // --------------------------------------------------
    // 2.2) Benutzer‑Login
    // --------------------------------------------------
    public function login(string $email, string $password)
    {
        try {
            // Benutzer anhand E‑Mail suchen (nur aktive Accounts)
            $stmt = $this->db->prepare(
                "SELECT * FROM admin_users WHERE email = ? AND is_active = 1"
            );
            $stmt->execute([$email]);
            ErrorLogger::logSqlSuccess('SELECT * FROM admin_users WHERE email = ? | params: ' . json_encode([$email], JSON_UNESCAPED_UNICODE));
            $user = $stmt->fetch();
        } catch (PDOException $e) {
            ErrorLogger::logSqlError('SELECT * FROM admin_users WHERE email = ? | params: ' . json_encode([$email], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
            return 'Fehler bei der Anmeldung.';
        }

        // -----------------------------------------------------------------
        // Zugriff verweigern, wenn Benutzer nicht existiert oder gesperrt
        // -----------------------------------------------------------------
        if (!$user || $user['failed_logins'] >= 5) {
            return 'Zugang gesperrt oder Benutzer nicht gefunden.';
        }

        // -----------------------------------------------------------------
        // Passwort validieren
        // -----------------------------------------------------------------
        if (password_verify($password, $user['password_hash'])) {
            try {
                // Reset der Fehlversuche & Timestamp setzen
                $this->db->prepare(
                    'UPDATE admin_users SET failed_logins = 0, last_login_attempt = NOW() WHERE id = ?'
                )->execute([$user['id']]);
                ErrorLogger::logSqlSuccess('UPDATE admin_users SET failed_logins = 0... | params: ' . json_encode([$user['id']], JSON_UNESCAPED_UNICODE));
            } catch (PDOException $e) {
                ErrorLogger::logSqlError('UPDATE admin_users SET failed_logins = 0... | params: ' . json_encode([$user['id']], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
                return 'Fehler beim Zurücksetzen der Fehlversuche.';
            }

            // ----------------------
            // Session setzen
            // ----------------------
            $_SESSION['admin_id']    = $user['id'];
            $_SESSION['admin_role']  = $user['role'];
            $_SESSION['admin_email'] = $user['email'];

            // ----------------------
            // User‑Aktion loggen
            // ----------------------
            logUserAction($user['id'], 'Login erfolgreich', 'IP: ' . ($_SERVER['REMOTE_ADDR'] ?? 'CLI'));

            return true; // Erfolg
        }

        // -----------------------------------------------------------------
        // Passwort falsch → Fehlversuchs‑Counter erhöhen
        // -----------------------------------------------------------------
        try {
            $this->db->prepare(
                'UPDATE admin_users SET failed_logins = failed_logins + 1, last_login_attempt = NOW() WHERE id = ?'
            )->execute([$user['id']]);
            ErrorLogger::logSqlSuccess('UPDATE admin_users SET failed_logins + 1... | params: ' . json_encode([$user['id']], JSON_UNESCAPED_UNICODE));
        } catch (PDOException $e) {
            ErrorLogger::logSqlError('UPDATE admin_users SET failed_logins + 1... | params: ' . json_encode([$user['id']], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
        }

        logUserAction($user['id'], 'Login fehlgeschlagen', 'IP: ' . ($_SERVER['REMOTE_ADDR'] ?? 'CLI'));
        return 'Login fehlgeschlagen.';
    }

    // --------------------------------------------------
    // 2.3) Logout – Session beenden und Aktion loggen
    // --------------------------------------------------
    public function logout(): void
    {
        if (isset($_SESSION['admin_id'])) {
            logUserAction($_SESSION['admin_id'], 'Logout', 'User hat sich abgemeldet.');
        }
        session_destroy();
    }

    // --------------------------------------------------
    // 2.4) Prüft Login‑Status
    // --------------------------------------------------
    public function isLoggedIn(): bool
    {
        return isset($_SESSION['admin_id']);
    }

    // --------------------------------------------------
    // 2.5) Passwort‑Reset anfordern
    // --------------------------------------------------
    public function requestPasswordReset(string $email)
    {
        try {
            $stmt = $this->db->prepare(
                'SELECT * FROM admin_users WHERE email = ? AND is_active = 1'
            );
            $stmt->execute([$email]);
            ErrorLogger::logSqlSuccess('SELECT * FROM admin_users WHERE email = ? | params: ' . json_encode([$email], JSON_UNESCAPED_UNICODE));
            $user = $stmt->fetch();
        } catch (PDOException $e) {
            ErrorLogger::logSqlError('SELECT * FROM admin_users WHERE email = ? | params: ' . json_encode([$email], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
            return false;
        }

        if (!$user) {
            ErrorLogger::log('Passwort‑Reset: Benutzer nicht gefunden – ' . $email);
            return false;
        }

        // ---------- Reset‑Token erstellen ----------
        $token   = bin2hex(random_bytes(16));
        $expires = (new DateTime('+1 hour'))->format('Y-m-d H:i:s');
        ErrorLogger::log("Reset‑Token erstellt: $token gültig bis $expires");

        try {
            $insert = $this->db->prepare(
                'INSERT INTO password_resets (email, reset_token, expires_at, used) VALUES (?, ?, ?, 0)'
            );
            $insert->execute([$email, $token, $expires]);
            ErrorLogger::logSqlSuccess('INSERT INTO password_resets ... | params: ' . json_encode([$email, $token, $expires], JSON_UNESCAPED_UNICODE));
        } catch (PDOException $e) {
            ErrorLogger::logSqlError('INSERT INTO password_resets ... | params: ' . json_encode([$email, $token, $expires], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
            return false;
        }

        // Abgelaufene/verwendete Tokens säubern (nicht kritisch bei Fehler)
        try {
            $this->db->prepare(
                'DELETE FROM password_resets WHERE expires_at < DATE_SUB(NOW(), INTERVAL 1 MINUTE) OR used = 1'
            )->execute();
            ErrorLogger::logSqlSuccess('DELETE alte Reset-Tokens | params: []');
        } catch (PDOException $e) {
            ErrorLogger::logSqlError('DELETE alte Reset‑Tokens | params: ' . json_encode([], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
        }

        // ---------- Mail versenden ----------
        $resetLink = rtrim($this->config['app']['base_url'], '/') . "/admin/reset.php?token=$token";
        $subject   = 'Passwort zurücksetzen';
        $body      = "<html><head><meta charset='UTF-8'></head><body>
                        <p>Hallo,</p>
                        <p>Klicke auf den folgenden Link, um dein Passwort zurückzusetzen:</p>
                        <p><a href='$resetLink'>$resetLink</a></p>
                        <p>Dieser Link ist 1 Stunde gültig.</p>
                      </body></html>";

        $mailer = new Mailer();
        $mailer->setCharset('UTF-8');
        $mailOk = $mailer->send($email, $subject, $body);
        ErrorLogger::log('Mailer‑Result für ' . $email . ': ' . var_export($mailOk, true));

        // Aktion protokollieren
        logUserAction($user['id'], 'Passwort‑Reset angefordert', 'Token: ' . $token);

        return $mailOk;
    }

    // --------------------------------------------------
    // 2.6) Passwort mithilfe Token zurücksetzen
    // --------------------------------------------------
    public function resetPassword(string $token, string $newPassword): bool
    {
        try {
            $stmt = $this->db->prepare(
                'SELECT * FROM password_resets WHERE reset_token = ? AND used = 0'
            );
            $stmt->execute([$token]);
            ErrorLogger::logSqlSuccess('SELECT * FROM password_resets WHERE token | params: ' . json_encode([$token], JSON_UNESCAPED_UNICODE));
            $tokenRow = $stmt->fetch();
        } catch (PDOException $e) {
            ErrorLogger::logSqlError('SELECT * FROM password_resets WHERE token | params: ' . json_encode([$token], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
            return false;
        }

        if (!$tokenRow) {
            return false; // Token ungültig oder bereits benutzt
        }

        // Token‑Ablauf prüfen
        $expiresAt = DateTime::createFromFormat('Y-m-d H:i:s', $tokenRow['expires_at']);
        if (!$expiresAt || new DateTime('now') > $expiresAt) {
            return false;
        }

        // ---------------- Passwort aktualisieren ----------------
        try {
            $hash = password_hash($newPassword, PASSWORD_DEFAULT);
            $update = $this->db->prepare(
                'UPDATE admin_users SET password_hash = ? WHERE email = ?'
            );
            $update->execute([$hash, $tokenRow['email']]);
            ErrorLogger::logSqlSuccess('UPDATE admin_users SET password_hash | params: ' . json_encode([$tokenRow['email']], JSON_UNESCAPED_UNICODE));
        } catch (PDOException $e) {
            ErrorLogger::logSqlError('UPDATE admin_users SET password_hash | params: ' . json_encode([$tokenRow['email']], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
            return false;
        }

        // ---------------- Token als verwendet markieren ----------------
        try {
            $this->db->prepare(
                'UPDATE password_resets SET used = 1 WHERE id = ?'
            )->execute([$tokenRow['id']]);
            ErrorLogger::logSqlSuccess('UPDATE password_resets SET used = 1 | params: ' . json_encode([$tokenRow['id']], JSON_UNESCAPED_UNICODE));
        } catch (PDOException $e) {
            ErrorLogger::logSqlError('UPDATE password_resets SET used = 1 | params: ' . json_encode([$tokenRow['id']], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
            return false;
        }

        // ---------------- Benutzer‑ID ermitteln & Aktion loggen ----------------
        try {
            $uidStmt = $this->db->prepare('SELECT id FROM admin_users WHERE email = ?');
            $uidStmt->execute([$tokenRow['email']]);
            ErrorLogger::logSqlSuccess('SELECT id FROM admin_users WHERE email | params: ' . json_encode([$tokenRow['email']], JSON_UNESCAPED_UNICODE));
            $uid = ($tmp = $uidStmt->fetchColumn()) ? (int)$tmp : 0;
        } catch (PDOException $e) {
            ErrorLogger::logSqlError('SELECT id FROM admin_users WHERE email | params: ' . json_encode([$tokenRow['email']], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
            $uid = 0;
        }

        logUserAction($uid, 'Passwort zurückgesetzt (Token)', 'Token‑ID: ' . $tokenRow['id']);
        return true;
    }

    // --------------------------------------------------
    // Optionale Erweiterung (Beispiel): Einstellungen ändern & loggen
    // --------------------------------------------------
    public function updateSetting(int $userId, string $key, string $value): bool
    {
        try {
            $stmt = $this->db->prepare(
                'UPDATE user_settings SET setting_value = ? WHERE user_id = ? AND setting_key = ?'
            );
            $stmt->execute([$value, $userId, $key]);
            ErrorLogger::logSqlSuccess('UPDATE user_settings | params: ' . json_encode([$userId, $key, $value], JSON_UNESCAPED_UNICODE));
        } catch (PDOException $e) {
            ErrorLogger::logSqlError('UPDATE user_settings | params: ' . json_encode([$userId, $key, $value], JSON_UNESCAPED_UNICODE) . ' | error: ' . $e->getMessage());
            return false;
        }

        // Setting‑Änderung protokollieren
        logSettingChange($userId, 'Setting geändert', $key, 'Neuer Wert: ' . $value);
        return true;
    }
}


