<?php

namespace App\Services\Marketplace;

use App\Models\MarketplaceInstalled;
use App\Models\Setting;

/**
 * Checks installed marketplace packages against the registry for available updates.
 *
 * Results are cached in storage/cache/marketplace_updates.json (TTL configurable).
 * The dashboard reads getCached() – no network call on every page load.
 */
class UpdateChecker
{
    private string $basePath;
    private string $cacheFile;
    private int    $ttl;

    public function __construct(string $basePath)
    {
        $this->basePath  = rtrim($basePath, '/\\');
        $this->cacheFile = $this->basePath . '/storage/cache/marketplace_updates.json';
        $this->ttl       = (int) Setting::get('marketplace_update_check_ttl', '21600'); // 6h default
    }

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

    /**
     * Run a fresh update check and write results to cache.
     *
     * @return array<array{slug: string, name: string, version_installed: string,
     *                      version_latest: string, type: string, security: bool}>
     */
    public function run(): array
    {
        $service = new MarketplaceService($this->basePath);

        if (!$service->isConfigured()) {
            $this->writeCache([]);
            return [];
        }

        $registry = $service->fetchRegistry();
        if (!$registry['success'] || empty($registry['packages'])) {
            return $this->getCached(); // Return stale if available
        }

        $installed = MarketplaceInstalled::getInstalledMap();
        $updates   = [];

        foreach ($registry['packages'] as $pkg) {
            $slug = $pkg['slug'] ?? '';
            if (!isset($installed[$slug])) {
                continue;
            }

            $localVersion  = $installed[$slug]['version']  ?? '0.0.0';
            $remoteVersion = $pkg['version']               ?? '0.0.0';

            if (!version_compare($remoteVersion, $localVersion, '>')) {
                continue;
            }

            // Security flag: if new version requires higher CMS than current
            $minCmsRequired = $pkg['min_cms_version'] ?? '0';
            $security       = $minCmsRequired !== '0' && !version_compare(cms_version(), $minCmsRequired, '>=');

            $updates[] = [
                'slug'              => $slug,
                'name'              => $pkg['name']  ?? $slug,
                'type'              => $pkg['type']  ?? 'plugin',
                'version_installed' => $localVersion,
                'version_latest'    => $remoteVersion,
                'security'          => $security,
                'min_cms_version'   => $minCmsRequired,
            ];
        }

        $this->writeCache($updates);
        return $updates;
    }

    /**
     * Return cached update list without making a network call.
     * Returns empty array if cache is missing or expired.
     *
     * @return array<array{slug: string, name: string, version_installed: string,
     *                      version_latest: string, type: string, security: bool}>
     */
    public function getCached(): array
    {
        if (!is_file($this->cacheFile)) {
            return [];
        }

        $raw   = file_get_contents($this->cacheFile);
        $cache = json_decode($raw, true);

        if (!is_array($cache) || !isset($cache['_timestamp'])) {
            return [];
        }

        $age = time() - (int) $cache['_timestamp'];
        if ($age > $this->ttl) {
            return []; // Expired
        }

        return $cache['updates'] ?? [];
    }

    /**
     * Return cached updates if available, or trigger a fresh run if stale.
     * Does NOT block – if the run fails it falls back to stale data gracefully.
     */
    public function getOrRefresh(): array
    {
        if (!is_file($this->cacheFile)) {
            try {
                return $this->run();
            } catch (\Throwable $e) {
                return [];
            }
        }

        $raw   = file_get_contents($this->cacheFile);
        $cache = json_decode($raw, true);

        $age = time() - (int) ($cache['_timestamp'] ?? 0);
        if ($age > $this->ttl) {
            try {
                return $this->run();
            } catch (\Throwable $e) {
                return $cache['updates'] ?? []; // Fall back to stale
            }
        }

        return $cache['updates'] ?? [];
    }

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

    private function writeCache(array $updates): void
    {
        $cacheDir = dirname($this->cacheFile);
        if (!is_dir($cacheDir)) {
            @mkdir($cacheDir, 0755, true);
        }

        file_put_contents($this->cacheFile, json_encode([
            '_timestamp' => time(),
            'updates'    => $updates,
        ], JSON_UNESCAPED_UNICODE));
    }
}
