<?php

namespace App\Http\Controllers\Admin;

use App\Core\Controller;
use App\Core\Database;
use App\Core\RateLimiter;
use App\Core\Request;
use App\Core\Response;
use App\Models\Setting;
use App\Services\CmsUpdate\CmsUpdateChecker;
use App\Services\CmsUpdate\CmsUpdateService;

/**
 * ITSWEBER CMS – System Update Controller
 *
 * Handles CMS Core auto-update flow:
 *   GET  /admin/system/updates          → index  (status + install panel)
 *   POST /admin/system/updates/check    → check  (trigger manual check)
 *   POST /admin/system/updates/install  → install (start update install)
 *   GET  /admin/system/updates/status   → status (JSON for JS polling)
 *   POST /admin/system/updates/rollback → rollback (manual rollback from pre-backup)
 */
class SystemUpdateController extends Controller
{
    // ─────────────────────────────────────────────────────────
    // GET /admin/system/updates
    // ─────────────────────────────────────────────────────────

    public function index(Request $request, array $params = []): void
    {
        $checker   = new CmsUpdateChecker(BASE_PATH);
        $updateInfo = $checker->getCached();

        $lastLog = Setting::get('cms_update_last_install_log', '');
        $lastInstall = $lastLog !== '' ? (json_decode($lastLog, true) ?: []) : [];

        $preBackup = Setting::get('cms_update_last_pre_backup', '');

        $this->renderAdmin('system-updates/index', [
            'pageTitle'       => __('admin.system_updates.title'),
            'updateInfo'      => $updateInfo,
            'currentVersion'  => function_exists('cms_version') ? cms_version() : '',
            'lastInstall'     => $lastInstall,
            'preBackupExists' => ($preBackup !== '' && is_file($preBackup)),
            'preBackupPath'   => $preBackup,
            'checkEnabled'    => Setting::get('cms_update_check_enabled', '1') === '1',
            'registryUrl'     => Setting::get('cms_update_registry_url', 'https://itsweber.de'),
            'lastCheckedAt'   => Setting::get('cms_update_last_check_at', ''),
            'auth'            => $this->auth(),
            'csrf'            => $this->csrf(),
        ]);
    }

    // ─────────────────────────────────────────────────────────
    // POST /admin/system/updates/check
    // ─────────────────────────────────────────────────────────

    public function check(Request $request, array $params = []): void
    {
        $this->requireAdmin();
        $this->validateCsrf();

        $checker = new CmsUpdateChecker(BASE_PATH);
        $result  = $checker->run();

        if ($result['available']) {
            set_flash('success', __('admin.system_updates.update_available', ['version' => $result['version']]));
        } else {
            set_flash('info', __('admin.system_updates.up_to_date'));
        }

        Response::redirect(admin_url('system/updates'));
    }

    // ─────────────────────────────────────────────────────────
    // POST /admin/system/updates/install
    // ─────────────────────────────────────────────────────────

    public function install(Request $request, array $params = []): void
    {
        $this->requireAdmin();
        $this->validateCsrf();

        // F6 – Rate-Limit: max 3 Install-Triggers pro 10min je Admin-User.
        // DoS-Schutz gegen Mass-Install via XSS oder Session-Takeover.
        $limiter = new RateLimiter(BASE_PATH . '/storage/cache', 3, 600, 600);
        $userId  = $this->auth() ? ($this->auth()->userId() ?? 0) : 0;
        $key     = 'cms_update_install:' . $userId;
        if ($limiter->isLocked($key)) {
            $remaining = max(1, (int) ceil($limiter->lockoutRemaining($key) / 60));
            Response::json([
                'success' => false,
                'error'   => 'Zu viele Installations-Versuche. Bitte in ' . $remaining . ' Minute(n) erneut versuchen.',
            ], 429);
            return;
        }
        $limiter->recordFailure($key);

        // Guard: maintenance mode
        if (class_exists(\App\Services\MaintenanceService::class)) {
            if (\App\Services\MaintenanceService::isActive(BASE_PATH)) {
                Response::json(['success' => false, 'error' => 'CMS ist im Wartungsmodus. Bitte deaktivieren und erneut versuchen.'], 409);
                return;
            }
        }

        $checker    = new CmsUpdateChecker(BASE_PATH);
        $updateInfo = $checker->getCached();

        if (!$updateInfo['available'] || empty($updateInfo['data'])) {
            Response::json(['success' => false, 'error' => 'Kein Update verfügbar.'], 400);
            return;
        }

        $includeUploads = $request->input('backup_uploads', '0') === '1';
        $backupScope    = ['uploads' => $includeUploads];

        $db      = $this->db();
        $service = new CmsUpdateService(BASE_PATH, $db);

        // Run install (synchronous – JS polls /status)
        $result = $service->install($updateInfo['data'], $backupScope);

        Response::json($result);
    }

    // ─────────────────────────────────────────────────────────
    // GET /admin/system/updates/status
    // ─────────────────────────────────────────────────────────

    public function status(Request $request, array $params = []): void
    {
        $progressFile = BASE_PATH . '/storage/temp/cms_update_progress.json';

        if (!is_file($progressFile)) {
            Response::json(['step' => 0, 'label' => '', 'status' => 'idle', 'percent' => 0]);
            return;
        }

        $data = json_decode((string) file_get_contents($progressFile), true);
        Response::json(is_array($data) ? $data : ['step' => 0, 'label' => '', 'status' => 'unknown', 'percent' => 0]);
    }

    // ─────────────────────────────────────────────────────────
    // POST /admin/system/updates/rollback
    // ─────────────────────────────────────────────────────────

    public function rollback(Request $request, array $params = []): void
    {
        $this->requireAdmin();
        $this->validateCsrf();

        $preBackup = Setting::get('cms_update_last_pre_backup', '');

        if ($preBackup === '' || !is_file($preBackup)) {
            set_flash('error', __('admin.system_updates.no_backup_for_rollback'));
            Response::redirect(admin_url('system/updates'));
            return;
        }

        try {
            $db = $this->db();
            \App\Services\BackupService::restore($preBackup, $db, BASE_PATH);
            set_flash('success', __('admin.system_updates.rollback_succeeded'));
        } catch (\Throwable $e) {
            set_flash('error', __('admin.system_updates.rollback_failed') . ' ' . $e->getMessage());
        }

        Response::redirect(admin_url('system/updates'));
    }

    // ─────────────────────────────────────────────────────────
    // Private helpers
    // ─────────────────────────────────────────────────────────

    protected function requireAdmin(): void
    {
        $auth = $this->auth();
        if (!$auth || !$auth->check() || !$auth->isAdmin()) {
            $isAjax = (
                (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest')
                || (isset($_SERVER['HTTP_ACCEPT']) && str_contains($_SERVER['HTTP_ACCEPT'], 'application/json'))
            );
            if ($isAjax) {
                Response::json(['success' => false, 'error' => 'Unauthorized'], 403);
            } else {
                Response::redirect(admin_url('login'));
            }
            exit;
        }
    }
}
