<?php
/**
 * Admin notice when a newer maca Restu Pro release is published on maca.se.
 *
 * @package Maca_Menu_List_Pro
 */

if (!defined('ABSPATH')) {
    exit;
}

class Maca_MenuList_Update_Notice {

    const TRANSIENT_KEY = 'maca_menulist_pro_update_info';
    const TRANSIENT_TTL = 43200; // 12 hours
    const TRANSIENT_FAIL_TTL = 3600; // 1 hour after failed fetch

    /**
     * @var self|null
     */
    private static $instance = null;

    /**
     * @return self
     */
    public static function instance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {
        add_action('admin_notices', array($this, 'render_notice'));
        add_action(Maca_MenuList_License::CRON_HOOK, array($this, 'refresh_cache'), 20);
        add_action('admin_init', array($this, 'maybe_refresh_on_admin'));
    }

    /**
     * Default product page on maca.se (download / release info).
     *
     * @return string
     */
    public function get_default_info_url() {
        return apply_filters(
            'maca_menulist_update_info_url',
            'https://maca.se/restu-pro/'
        );
    }

    /**
     * REST endpoint that returns latest Pro version metadata.
     *
     * @return string
     */
    public function get_updates_api_url() {
        return apply_filters(
            'maca_menulist_updates_api_url',
            'https://maca.se/wp-json/maca-menulist/v1/updates'
        );
    }

    /**
     * Refresh cached update info (daily license cron).
     *
     * @return void
     */
    public function refresh_cache() {
        $this->fetch_and_cache();
    }

    /**
     * Fetch once per admin request when cache is empty.
     *
     * @return void
     */
    public function maybe_refresh_on_admin() {
        if (!is_admin() || !maca_menulist_user_can_manage_plugin()) {
            return;
        }

        $cached = get_transient(self::TRANSIENT_KEY);
        if (is_array($cached) && !empty($cached['fetched_at'])) {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only screen detection.
        $page = isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : '';
        if ($page === '' || !maca_menulist_is_maca_restu_admin_page($page)) {
            return;
        }

        $this->fetch_and_cache();
    }

    /**
     * @return array{version: string, info_url: string}
     */
    private function get_cached_info() {
        $cached = get_transient(self::TRANSIENT_KEY);
        if (!is_array($cached)) {
            return array(
                'version'  => '',
                'info_url' => $this->get_default_info_url(),
            );
        }

        return array(
            'version'  => isset($cached['version']) ? (string) $cached['version'] : '',
            'info_url' => !empty($cached['info_url'])
                ? (string) $cached['info_url']
                : $this->get_default_info_url(),
        );
    }

    /**
     * @return void
     */
    private function fetch_and_cache() {
        $response = wp_remote_get(
            $this->get_updates_api_url(),
            array(
                'timeout' => 12,
                'headers' => array(
                    'Accept' => 'application/json',
                ),
            )
        );

        if (is_wp_error($response)) {
            $this->store_cache(array(), self::TRANSIENT_FAIL_TTL);
            return;
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        if ($code < 200 || $code >= 300) {
            $this->store_cache(array(), self::TRANSIENT_FAIL_TTL);
            return;
        }

        $json = json_decode(wp_remote_retrieve_body($response), true);
        if (!is_array($json)) {
            $this->store_cache(array(), self::TRANSIENT_FAIL_TTL);
            return;
        }

        $version = isset($json['version']) ? $this->normalize_version($json['version']) : '';
        if ($version === '') {
            $this->store_cache(array(), self::TRANSIENT_FAIL_TTL);
            return;
        }

        $info_url = !empty($json['info_url'])
            ? esc_url_raw((string) $json['info_url'])
            : $this->get_default_info_url();

        $this->store_cache(
            array(
                'version'  => $version,
                'info_url' => $info_url !== '' ? $info_url : $this->get_default_info_url(),
            ),
            self::TRANSIENT_TTL
        );
    }

    /**
     * @param array<string, mixed> $data Cached payload.
     * @param int                  $ttl  Seconds.
     * @return void
     */
    private function store_cache($data, $ttl) {
        $payload = array(
            'version'   => isset($data['version']) ? (string) $data['version'] : '',
            'info_url'  => isset($data['info_url']) ? (string) $data['info_url'] : '',
            'fetched_at' => time(),
        );

        set_transient(self::TRANSIENT_KEY, $payload, max(60, (int) $ttl));
    }

    /**
     * @param mixed $version Raw version string.
     * @return string
     */
    private function normalize_version($version) {
        $version = trim(sanitize_text_field((string) $version));
        if ($version === '' || !preg_match('/^\d+\.\d+(\.\d+)?/', $version)) {
            return '';
        }

        return $version;
    }

    /**
     * Whether a newer release is available than this install.
     *
     * @return array{show: bool, latest: string, info_url: string}
     */
    private function get_availability() {
        if (!defined('MACA_MENULIST_VERSION')) {
            return array(
                'show'     => false,
                'latest'   => '',
                'info_url' => $this->get_default_info_url(),
            );
        }

        $info   = $this->get_cached_info();
        $latest = $info['version'];

        if ($latest === '' || !version_compare(MACA_MENULIST_VERSION, $latest, '<')) {
            return array(
                'show'     => false,
                'latest'   => $latest,
                'info_url' => $info['info_url'],
            );
        }

        return array(
            'show'     => true,
            'latest'   => $latest,
            'info_url' => $info['info_url'],
        );
    }

    /**
     * Admin banner for site administrators.
     *
     * @return void
     */
    public function render_notice() {
        if (!is_admin() || !maca_menulist_user_can_manage_plugin()) {
            return;
        }

        if (!defined('MACA_MENULIST_PRO') || !MACA_MENULIST_PRO) {
            return;
        }

        $availability = $this->get_availability();
        if (!$availability['show']) {
            return;
        }

        $message = sprintf(
            /* translators: 1: latest version on maca.se, 2: installed version */
            __(
                'A new version of maca Restu Pro is available (%1$s). You are running %2$s.',
                'maca-restu-pro'
            ),
            $availability['latest'],
            MACA_MENULIST_VERSION
        );

        echo '<div class="notice notice-info"><p><strong>' . esc_html__('Update available:', 'maca-restu-pro') . '</strong> ';
        echo esc_html($message) . ' ';
        echo '<a href="' . esc_url($availability['info_url']) . '" target="_blank" rel="noopener noreferrer">';
        esc_html_e('Download and instructions on maca.se', 'maca-restu-pro');
        echo '</a></p></div>';
    }
}

if (defined('MACA_MENULIST_UPDATES_API_URL') && MACA_MENULIST_UPDATES_API_URL) {
    add_filter(
        'maca_menulist_updates_api_url',
        static function () {
            return MACA_MENULIST_UPDATES_API_URL;
        }
    );
}
