<?php
/**
 * SailWP Co-pilot — self-hosted update checker.
 *
 * The plugin lives outside the wp.org repo, so WordPress will not poll
 * for updates on its own. This class hooks into the same transient
 * pipeline that core uses (`update_plugins` site_transient + the
 * `plugins_api` filter) and points it at a small JSON manifest hosted
 * on sailwp.com.
 *
 * Manifest shape (kept tiny on purpose):
 *   {
 *     "version":      "0.17.1",
 *     "download_url": "https://sailwp.com/sailwp-ai-copilot.zip",
 *     "requires":     "6.0",
 *     "tested":       "6.7",
 *     "requires_php": "7.4",
 *     "last_updated": "2026-05-09 20:00:00",
 *     "homepage":     "https://sailwp.com",
 *     "sections": {
 *       "description": "...",
 *       "changelog":   "..."
 *     }
 *   }
 *
 * Manifest is cached in a transient for 6 hours; a hard refresh fires
 * automatically when the user clicks "Check again" on Plugins → Updates,
 * since core clears `update_plugins` before re-running the filters.
 */
if (!defined('ABSPATH')) exit;

class SailWP_Copilot_Update_Checker {
    const MANIFEST_URL  = 'https://sailwp.com/plugin-update.json';
    const CACHE_KEY     = 'swcp_update_manifest';
    const CACHE_TTL     = 6 * HOUR_IN_SECONDS;

    private $plugin_file;       // absolute path
    private $plugin_basename;   // 'sailwp-ai-copilot/sailwp-ai-copilot.php'
    private $plugin_slug;       // 'sailwp-ai-copilot'
    private $current_version;

    public function __construct($plugin_file) {
        $this->plugin_file     = $plugin_file;
        $this->plugin_basename = plugin_basename($plugin_file);
        $this->plugin_slug     = dirname($this->plugin_basename);
        $this->current_version = defined('SAILWP_COPILOT_VERSION') ? SAILWP_COPILOT_VERSION : '0.0.0';

        add_filter('pre_set_site_transient_update_plugins', [$this, 'inject_update']);
        add_filter('plugins_api', [$this, 'plugins_api'], 20, 3);
        // Force WordPress' background auto-update job to upgrade this
        // plugin without waiting for a click. Joost asked for this on
        // 2026-05-15 ("zolang de huidige gebruikers maar automatisch
        // worden doorgebracht"): once a site is on a build that
        // contains this filter, every subsequent release ships
        // silently via wp-cron's twicedaily auto-update sweep — the
        // user never has to visit Plugins → Updates and click.
        add_filter('auto_update_plugin', [$this, 'force_auto_update'], 10, 2);
        // Clear the manifest cache after a successful upgrade so we don't
        // keep advertising the version we just installed.
        add_action('upgrader_process_complete', [$this, 'on_upgrade'], 10, 2);
    }

    /**
     * Return true for our own plugin so WordPress' auto-update
     * scheduler picks it up without an explicit per-site opt-in.
     * Other plugins keep whatever decision $update already carries.
     */
    public function force_auto_update($update, $item) {
        if (is_object($item) && isset($item->plugin) && $item->plugin === $this->plugin_basename) {
            return true;
        }
        return $update;
    }

    /**
     * Tell WordPress about a newer version when one is available.
     */
    public function inject_update($transient) {
        if (!is_object($transient)) return $transient;

        $manifest = $this->get_manifest();
        if (!$manifest) return $transient;
        if (empty($manifest['version']) || empty($manifest['download_url'])) return $transient;
        if (version_compare($manifest['version'], $this->current_version, '<=')) return $transient;

        $update = (object) [
            'id'            => 'sailwp.com/' . $this->plugin_basename,
            'slug'          => $this->plugin_slug,
            'plugin'        => $this->plugin_basename,
            'new_version'   => (string) $manifest['version'],
            'url'           => isset($manifest['homepage']) ? $manifest['homepage'] : 'https://sailwp.com',
            'package'       => (string) $manifest['download_url'],
            'icons'         => [],
            'banners'       => [],
            'banners_rtl'   => [],
            'tested'        => isset($manifest['tested'])       ? (string) $manifest['tested']       : '',
            'requires_php'  => isset($manifest['requires_php']) ? (string) $manifest['requires_php'] : '',
            'compatibility' => new stdClass(),
        ];

        if (!isset($transient->response) || !is_array($transient->response)) {
            $transient->response = [];
        }
        $transient->response[$this->plugin_basename] = $update;
        return $transient;
    }

    /**
     * Power the "View details" modal on Plugins → Updates so the user
     * sees a real changelog before they hit Update Now.
     */
    public function plugins_api($result, $action, $args) {
        if ($action !== 'plugin_information') return $result;
        if (empty($args->slug) || $args->slug !== $this->plugin_slug) return $result;

        $manifest = $this->get_manifest();
        if (!$manifest) return $result;

        $sections = isset($manifest['sections']) && is_array($manifest['sections']) ? $manifest['sections'] : [];

        return (object) [
            'name'           => 'SailWP AI Co-pilot',
            'slug'           => $this->plugin_slug,
            'version'        => isset($manifest['version']) ? (string) $manifest['version'] : '',
            'author'         => '<a href="https://sailwp.com">Joost Boer</a>',
            'homepage'       => isset($manifest['homepage']) ? $manifest['homepage'] : 'https://sailwp.com',
            'requires'       => isset($manifest['requires'])     ? (string) $manifest['requires']     : '',
            'tested'         => isset($manifest['tested'])       ? (string) $manifest['tested']       : '',
            'requires_php'   => isset($manifest['requires_php']) ? (string) $manifest['requires_php'] : '',
            'last_updated'   => isset($manifest['last_updated']) ? (string) $manifest['last_updated'] : '',
            'download_link'  => isset($manifest['download_url']) ? (string) $manifest['download_url'] : '',
            'sections'       => $sections,
        ];
    }

    public function on_upgrade($upgrader, $hook_extra) {
        if (!is_array($hook_extra)) return;
        if (($hook_extra['action'] ?? '') !== 'update') return;
        if (($hook_extra['type'] ?? '') !== 'plugin') return;
        $plugins = $hook_extra['plugins'] ?? [];
        if (!is_array($plugins)) return;
        if (in_array($this->plugin_basename, $plugins, true)) {
            delete_site_transient(self::CACHE_KEY);
        }
    }

    private function get_manifest() {
        $cached = get_site_transient(self::CACHE_KEY);
        if (is_array($cached)) return $cached;

        $response = wp_remote_get(self::MANIFEST_URL, [
            'timeout' => 5,
            'headers' => ['Accept' => 'application/json'],
        ]);
        if (is_wp_error($response)) {
            // Cache a short-lived empty marker so we do not hammer the
            // endpoint when the network is having a bad day.
            set_site_transient(self::CACHE_KEY, ['version' => ''], 15 * MINUTE_IN_SECONDS);
            return null;
        }
        if (wp_remote_retrieve_response_code($response) !== 200) return null;

        $body = json_decode(wp_remote_retrieve_body($response), true);
        if (!is_array($body)) return null;

        set_site_transient(self::CACHE_KEY, $body, self::CACHE_TTL);
        return $body;
    }
}
