<?php

namespace App\Services\CmsUpdate;

use App\Models\Setting;

/**
 * Checks the CMS Core Update registry for a newer version.
 *
 * Results are cached in storage/cache/cms_core_update.json.
 * The dashboard and update page call getCached() – no network call on every load.
 * runIfDue() is triggered via pseudo-cron in App::handle().
 */
class CmsUpdateChecker
{
    private string $basePath;
    private string $cacheFile;

    public function __construct(string $basePath)
    {
        $this->basePath  = rtrim($basePath, '/\\');
        $this->cacheFile = $this->basePath . '/storage/cache/cms_core_update.json';
    }

    // ─────────────────────────────────────────────────────────
    // Public API
    // ─────────────────────────────────────────────────────────

    /**
     * Run a fresh check against the registry and write to cache.
     *
     * @return array{available: bool, version: string, data: array|null, checked_at: string}
     */
    public function run(): array
    {
        $registryUrl = rtrim(Setting::get('cms_update_registry_url', 'https://itsweber.de'), '/');
        $endpoint    = $registryUrl . '/api/v1/registry/cms-core/latest';

        $ctx = stream_context_create([
            'http' => [
                'method'          => 'GET',
                'timeout'         => 8,
                'follow_location' => 1,
                'user_agent'      => 'ITSWEBER-CMS/' . (defined('CMS_VERSION') ? CMS_VERSION : cms_version()),
                'header'          => 'Accept: application/json',
            ],
            'ssl' => [
                'verify_peer'      => true,
                'verify_peer_name' => true,
            ],
        ]);

        $result = ['available' => false, 'version' => '', 'data' => null, 'checked_at' => date('c')];

        try {
            $raw = @file_get_contents($endpoint, false, $ctx);
            if ($raw === false) {
                $this->writeCache($result);
                return $result;
            }

            $json = json_decode($raw, true);
            if (!isset($json['success']) || !$json['success'] || empty($json['data'])) {
                $this->writeCache($result);
                return $result;
            }

            $latest      = $json['data'];
            $latestVer   = $latest['version'] ?? '';
            $currentVer  = function_exists('cms_version') ? cms_version() : (defined('CMS_VERSION') ? CMS_VERSION : '0.0.0');

            if ($latestVer !== '' && version_compare($latestVer, $currentVer, '>')) {
                $result['available'] = true;
                $result['version']   = $latestVer;
                $result['data']      = $latest;
            }
        } catch (\Throwable $e) {
            error_log('CmsUpdateChecker: ' . $e->getMessage());
        }

        Setting::set('cms_update_last_check_at', date('c'), 'system_update');
        $this->writeCache($result);
        return $result;
    }

    /**
     * Return the cached result without a network call.
     *
     * @return array{available: bool, version: string, data: array|null, checked_at: string}
     */
    public function getCached(): array
    {
        if (!is_file($this->cacheFile)) {
            return ['available' => false, 'version' => '', 'data' => null, 'checked_at' => ''];
        }

        $data = json_decode((string) file_get_contents($this->cacheFile), true);
        if (!is_array($data)) {
            return ['available' => false, 'version' => '', 'data' => null, 'checked_at' => ''];
        }

        return $data;
    }

    /**
     * Run a fresh check only if the configured TTL has expired.
     */
    public function runIfDue(): void
    {
        if (Setting::get('cms_update_check_enabled', '1') !== '1') {
            return;
        }

        $ttl     = max(60, (int) Setting::get('cms_update_check_ttl', '21600'));
        $lastAt  = Setting::get('cms_update_last_check_at', '');
        $lastTs  = $lastAt !== '' ? strtotime($lastAt) : 0;

        if ((time() - $lastTs) < $ttl) {
            return;
        }

        try {
            $this->run();
        } catch (\Throwable $e) {
            error_log('CmsUpdateChecker::runIfDue: ' . $e->getMessage());
        }
    }

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

    private function writeCache(array $data): void
    {
        $dir = dirname($this->cacheFile);
        if (!is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }
        @file_put_contents($this->cacheFile, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
    }
}
