<?php
declare(strict_types=1);

/**
 * Routes for the DSGVO export plugin.
 *
 *   GET/POST /admin/plugins/gdpr           — settings page (admin)
 *   POST     /admin/users/{id}/gdpr-export — admin-triggered export
 *   POST     /settings/gdpr-export         — self-service export (if enabled)
 *
 * Access model:
 *   - Global admins can export any active user (path 1).
 *   - Every logged-in user can export ONLY their own data (path 2),
 *     and only when the operator has flipped the
 *     `gdpr_user_self_export_enabled` setting AND the per-user
 *     cooldown window (in hours) has elapsed.
 *   - Group admins (Provider Edition) get NO cross-tenant export
 *     rights — they go through the same self-service path as a normal
 *     user. Cross-tenant data extraction stays a global-admin operation.
 *
 * Every export is logged to the security log so the operator has an
 * audit trail (DSGVO Art. 30 — record of processing activities).
 */
class GdprController
{
    /** Hard cap for the cooldown setting — keeps the UI from accepting absurd values. */
    private const MAX_COOLDOWN_HOURS = 8760; // 1 year

    /** Soft cap on the README intro text. Long enough for a paragraph, short enough to fit a ZIP comment. */
    private const MAX_INTRO_LEN = 4000;

    // ── Admin: plugin settings ───────────────────────────────────────────────

    public static function adminSettings(): void
    {
        Auth::requireAdmin();
        $errors  = [];
        $success = Helper::flash('success');
        $error   = Helper::flash('error');

        if (Helper::isPost()) {
            Helper::verifyCsrf();
            self::handleSave($errors);
            if (empty($errors)) {
                Helper::redirect('/admin/plugins/gdpr', ['success' => Lang::get('gdpr_settings_saved')]);
            }
        }

        $cfg = self::loadCfgForView();
        view('admin/gdpr', compact('cfg', 'errors', 'success', 'error'));
    }

    private static function handleSave(array &$errors): void
    {
        $selfEnabled  = Helper::post('gdpr_user_self_export_enabled', '0') === '1' ? '1' : '0';
        $includeFiles = Helper::post('gdpr_include_attachments', '0') === '1' ? '1' : '0';

        $cooldownRaw = (int)Helper::post('gdpr_self_export_cooldown_hours', '24');
        if ($cooldownRaw < 1 || $cooldownRaw > self::MAX_COOLDOWN_HOURS) {
            $errors[] = Lang::get('gdpr_err_cooldown_range');
            return;
        }

        $intro = trim((string)Helper::post('gdpr_export_introduction_text', ''));
        if (mb_strlen($intro) > self::MAX_INTRO_LEN) {
            $errors[] = Lang::get('gdpr_err_intro_too_long');
            return;
        }

        Helper::setSetting('gdpr_user_self_export_enabled',  $selfEnabled);
        Helper::setSetting('gdpr_include_attachments',       $includeFiles);
        Helper::setSetting('gdpr_self_export_cooldown_hours', (string)$cooldownRaw);
        Helper::setSetting('gdpr_export_introduction_text',  $intro);

        SecurityLog::log('gdpr_settings_changed', SecurityLog::SEV_WARN, [
            'self_enabled'        => $selfEnabled,
            'include_attachments' => $includeFiles,
            'cooldown_hours'      => $cooldownRaw,
        ]);
    }

    private static function loadCfgForView(): array
    {
        return [
            'self_enabled'        => Helper::getSetting('gdpr_user_self_export_enabled', '0') === '1',
            'include_attachments' => Helper::getSetting('gdpr_include_attachments', '0') === '1',
            'cooldown_hours'      => max(1, (int)Helper::getSetting('gdpr_self_export_cooldown_hours', '24')),
            'introduction_text'   => (string)Helper::getSetting('gdpr_export_introduction_text', ''),
        ];
    }

    // ── Admin-triggered export for another user ──────────────────────────────

    public static function adminExport(int $subjectUserId): void
    {
        Auth::requireAdmin();
        if (!Helper::isPost()) {
            Helper::redirect('/admin/users');
        }
        Helper::verifyCsrf();

        if ($subjectUserId <= 0) {
            Helper::redirect('/admin/users', ['error' => Lang::get('not_found')]);
        }

        SecurityLog::log('gdpr_export_admin', SecurityLog::SEV_WARN, [
            'subject_user_id' => $subjectUserId,
            'actor_user_id'   => (int)(Auth::id() ?? 0),
        ]);

        // Streams the ZIP and exits. No code after this point.
        GdprExporter::buildAndStream($subjectUserId, true);
    }

    // ── Self-service export ──────────────────────────────────────────────────

    public static function userExport(): void
    {
        Auth::requireLogin();
        if (!Helper::isPost()) {
            Helper::redirect('/settings');
        }
        Helper::verifyCsrf();

        if (Helper::getSetting('gdpr_user_self_export_enabled', '0') !== '1') {
            // Admins can always self-export via /admin/users/{id}/gdpr-export,
            // so a normal user landing here when the toggle is off is either a
            // racing UI or a probing request — silent redirect is enough.
            Helper::redirect('/settings', ['error' => Lang::get('gdpr_self_export_disabled')]);
        }

        $uid = (int)(Auth::id() ?? 0);
        if ($uid <= 0) {
            Helper::redirect('/login');
        }

        $cooldownHours = max(1, (int)Helper::getSetting('gdpr_self_export_cooldown_hours', '24'));
        $lastTs        = self::lastSelfExportTs($uid);
        $nextOk        = $lastTs === 0 ? 0 : ($lastTs + $cooldownHours * 3600);
        if (time() < $nextOk) {
            $waitHours = (int)ceil(($nextOk - time()) / 3600);
            // Also log the throttle hit so a brute-force probe of the endpoint
            // shows up in the security log instead of silently bouncing.
            SecurityLog::log('gdpr_export_self_throttled', SecurityLog::SEV_INFO, [
                'wait_hours' => $waitHours,
            ]);
            Helper::redirect('/settings', ['error' => sprintf(Lang::get('gdpr_self_export_cooldown'), $waitHours)]);
        }

        SecurityLog::log('gdpr_export_self', SecurityLog::SEV_INFO, [
            'cooldown_hours' => $cooldownHours,
        ]);

        GdprExporter::buildAndStream($uid, false);
    }

    /**
     * Last successful self-export timestamp, used for cooldown enforcement.
     * Mirrors the lookup in GdprPlugin::lastSelfExportTs so the plugin's UI
     * card and this controller agree on the same window.
     */
    private static function lastSelfExportTs(int $userId): int
    {
        try {
            $row = Database::getInstance()->fetch(
                "SELECT created_at FROM security_events
                  WHERE event_type = 'gdpr_export_self' AND user_id = ?
                  ORDER BY created_at DESC LIMIT 1",
                [$userId]
            );
            return $row ? (int)strtotime((string)$row['created_at']) : 0;
        } catch (\Throwable) {
            return 0;
        }
    }
}
