<?php 
/**
 * WordPress Standard Plugin Update Checker
 * 
 * Requirements:
 * - Accepts update endpoint
 * - Accepts current plugin version, db version
 * - Checks for updates from endpoint
 * - Compares with plugin's own db, plugin version
 * - If update available, notifies WordPress plugin update system
 * - If debug mode enabled, shows endpoint status (200, 404, 500, timeout etc)
 * - Minimal admin notice with plugin's own name
 * 
 * @since 3.0.0
 */ 

class ST_Autoupdate {
    private $plugin_slug;
    private $plugin_name;
    private $version;
    private $db_version;
    private $update_url;
    private $plugin_file;
    private $plugin_basename;

    /**
     * Constructor
     * 
     * @param string $plugin_slug Plugin slug
     * @param string $plugin_name Plugin display name
     * @param string $version Current plugin version
     * @param string $db_version Current database version
     * @param string $update_url Update endpoint URL
     * @param string $plugin_file Plugin file path (e.g., 'spacetree/spacetree.php')
     */
    public function __construct($plugin_slug, $plugin_name, $version, $db_version, $update_url, $plugin_file) {
        $this->plugin_slug = $plugin_slug;
        $this->plugin_name = $plugin_name;
        $this->version = $version;
        $this->db_version = $db_version;
        $this->update_url = $update_url;
        $this->plugin_file = $plugin_file;
        $this->plugin_basename = plugin_basename($plugin_file);

        // WordPress standard update hooks
        add_filter('pre_set_site_transient_update_plugins', array($this, 'check_for_plugin_update'));
        add_filter('plugins_api', array($this, 'plugin_info'), 10, 3);
        add_filter('upgrader_source_selection', array($this, 'fix_plugin_folder_name'), 10, 3);
        
        // Clean up transient cache when needed
        add_action('admin_init', array($this, 'maybe_clear_update_cache'));
        
        // Show minimal admin notice for endpoint issues (only in debug mode)
        add_action('admin_notices', array($this, 'show_endpoint_notice'));
        
        // Clear cache when database version changes
        add_action('update_option_ST_DB_VERSION', array($this, 'clear_cache_on_db_version_change'));
    }

    /**
     * WordPress Standard: Check for plugin updates
     * Compares endpoint version with plugin version and db version
     */
    public function check_for_plugin_update($transient) {
        if (empty($transient->checked)) {
            return $transient;
        }

        // Get update info from custom endpoint
        $update_info = $this->get_update_info();
        
        if ($update_info && isset($update_info['version']) && isset($update_info['download_url'])) {
            // Compare plugin version
            $version_newer = version_compare($this->version, $update_info['version'], '<');
            
            // Compare db version if provided in update info
            $db_version_newer = false;
            if (isset($update_info['db_version'])) {
                $db_version_newer = version_compare($this->db_version, $update_info['db_version'], '<');
            }
            
            // If either version is newer, show update
            if ($version_newer || $db_version_newer) {
                // Prepare icons and banners from endpoint
                $icons = $this->prepare_icons($update_info);
                $banners = $this->prepare_banners($update_info);
                
                // Prepare update object for WordPress
                // Support new key names with fallback to old names for backward compatibility
                $wp_tested = isset($update_info['wp_tested_version']) ? $update_info['wp_tested_version'] : 
                            (isset($update_info['tested']) ? $update_info['tested'] : get_bloginfo('version'));
                
                $plugin_data = array(
                    'slug'        => $this->plugin_slug,
                    'plugin'      => $this->plugin_file,
                    'new_version' => $update_info['version'],
                    'url'         => isset($update_info['homepage']) ? $update_info['homepage'] : '',
                    'package'     => $update_info['download_url'],
                    'tested'      => $wp_tested,
                    'icons'       => $icons,
                    'banners'     => $banners,
                );

                // Add update info to transient
                $transient->response[$this->plugin_file] = (object) $plugin_data;
            }
        }

        return $transient;
    }

    /**
     * WordPress Standard: Provide plugin information
     * Shows in the "View Details" popup
     */
    public function plugin_info($result, $action, $args) {
        if ($action !== 'plugin_information') {
            return $result;
        }

        if (!isset($args->slug) || $args->slug !== $this->plugin_slug) {
            return $result;
        }

        // Get update info
        $update_info = $this->get_update_info();
        
        if (!$update_info) {
            return $result;
        }

        // Prepare plugin info object
        $plugin_info = new stdClass();
        $plugin_info->name = isset($update_info['name']) ? $update_info['name'] : $this->plugin_name;
        $plugin_info->slug = $this->plugin_slug;
        $plugin_info->version = isset($update_info['version']) ? $update_info['version'] : $this->version;
        $plugin_info->author = isset($update_info['author']) ? $update_info['author'] : '';
        $plugin_info->homepage = isset($update_info['homepage']) ? $update_info['homepage'] : '';
        $plugin_info->download_link = isset($update_info['download_url']) ? $update_info['download_url'] : '';
        
        // Support new key names with fallback to old names for backward compatibility
        $plugin_info->requires = isset($update_info['wp_min_version']) ? $update_info['wp_min_version'] : 
                                 (isset($update_info['requires']) ? $update_info['requires'] : '5.0');
        $plugin_info->tested = isset($update_info['wp_tested_version']) ? $update_info['wp_tested_version'] : 
                              (isset($update_info['tested']) ? $update_info['tested'] : get_bloginfo('version'));
        
        // Add PHP requirement if provided
        if (isset($update_info['php']) && !empty($update_info['php'])) {
            $plugin_info->requires_php = $update_info['php'];
        }
        
        $plugin_info->last_updated = isset($update_info['last_updated']) ? $update_info['last_updated'] : current_time('Y-m-d');
        
        // Sections: description and changelog from endpoint
        $plugin_info->sections = array();
        if (isset($update_info['description']) && !empty($update_info['description'])) {
            $plugin_info->sections['description'] = $update_info['description'];
        } else {
            $plugin_info->sections['description'] = $this->plugin_name . ' - Plugin update information.';
        }
        
        if (isset($update_info['changelog']) && !empty($update_info['changelog'])) {
            $plugin_info->sections['changelog'] = $update_info['changelog'];
        } else {
            $plugin_info->sections['changelog'] = $this->get_changelog($update_info);
        }
        
        // Icons from endpoint (format: { "1x": "url", "2x": "url", "default": "url" })
        $plugin_info->icons = $this->prepare_icons($update_info);
        
        // Banners from endpoint (format: { "low": "url", "high": "url" })
        $plugin_info->banners = array();
        if (isset($update_info['banners']) && is_array($update_info['banners'])) {
            $plugin_info->banners = $update_info['banners'];
        } else if (isset($update_info['banner'])) {
            // Fallback: single banner URL
            $plugin_info->banners = array(
                'low' => $update_info['banner'],
                'high' => $update_info['banner']
            );
        }

        return $plugin_info;
    }

    /**
     * WordPress Standard: Fix plugin folder name after extraction
     */
    public function fix_plugin_folder_name($source, $remote_source, $upgrader) {
        global $wp_filesystem;

        if (!isset($upgrader->skin->plugin) || $upgrader->skin->plugin !== $this->plugin_file) {
            return $source;
        }

        $correct_folder = dirname(WP_PLUGIN_DIR . '/' . $this->plugin_file);
        
        if ($source !== $correct_folder) {
            if ($wp_filesystem->exists($correct_folder)) {
                $wp_filesystem->delete($correct_folder, true);
            }
            
            if ($wp_filesystem->move($source, $correct_folder)) {
                return $correct_folder;
            }
        }

        return $source;
    }

    /**
     * Prepare icons array from update info
     * WordPress expects: default, 1x, 2x, and optionally svg
     */
    private function prepare_icons($update_info) {
        $icons = array();
        
        if (isset($update_info['icons']) && is_array($update_info['icons']) && !empty($update_info['icons'])) {
            $icons = $update_info['icons'];
            // Ensure default exists - WordPress requires at least a default icon
            if (empty($icons['default'])) {
                if (!empty($icons['1x'])) {
                    $icons['default'] = $icons['1x'];
                } else if (!empty($icons['2x'])) {
                    $icons['default'] = $icons['2x'];
                } else if (!empty($icons['svg'])) {
                    $icons['default'] = $icons['svg'];
                }
            }
        } else if (isset($update_info['icon']) && !empty($update_info['icon'])) {
            // Fallback: single icon URL
            $icons = array(
                'default' => $update_info['icon'],
                '1x' => $update_info['icon'],
                '2x' => $update_info['icon']
            );
        }
        
        return $icons;
    }
    
    /**
     * Prepare banners array from update info
     * WordPress expects: low (772x250) and high (1544x500)
     */
    private function prepare_banners($update_info) {
        $banners = array();
        
        if (isset($update_info['banners']) && is_array($update_info['banners']) && !empty($update_info['banners'])) {
            $banners = $update_info['banners'];
        } else if (isset($update_info['banner']) && !empty($update_info['banner'])) {
            // Fallback: single banner URL
            $banners = array(
                'low' => $update_info['banner'],
                'high' => $update_info['banner']
            );
        }
        
        return $banners;
    }

    /**
     * Get default changelog from update info
     * Used as fallback if endpoint doesn't provide changelog
     */
    private function get_changelog($update_info) {
        $version = isset($update_info['version']) ? $update_info['version'] : 'Latest';
        $changelog = '<h4>Version ' . esc_html($version) . '</h4>';
        
        if (isset($update_info['changelog_items']) && is_array($update_info['changelog_items'])) {
            $changelog .= '<ul>';
            foreach ($update_info['changelog_items'] as $item) {
                $changelog .= '<li>' . esc_html($item) . '</li>';
            }
            $changelog .= '</ul>';
        } else {
            $changelog .= '<ul><li>Update available</li></ul>';
        }
        
        return $changelog;
    }

    /**
     * Clear update cache manually if needed
     */
    public function maybe_clear_update_cache() {
        if (isset($_GET['force-check']) && $_GET['force-check'] === '1') {
            delete_site_transient('update_plugins');
            delete_transient('st_update_info_cache');
            delete_transient('st_update_endpoint_status');
        }
    }

    /**
     * Retrieve update information from custom endpoint
     * Tracks endpoint status for admin notices
     * 
     * Expected endpoint response format (JSON):
     * {
     *   "version": "3.0.1",
     *   "db_version": "3.1",
     *   "download_url": "https://example.com/plugin.zip",
     *   "name": "Plugin Name",
     *   "author": "Author Name",
     *   "homepage": "https://example.com",
     *   "wp_min_version": "5.0",
     *   "wp_tested_version": "6.3",
     *   "php": "7.4",
     *   "last_updated": "2024-01-01",
     *   "description": "<p>Plugin description HTML content</p>",
     *   "changelog": "<h4>Version 3.0.1</h4><ul><li>Feature 1</li><li>Bug fix</li></ul>",
     *   "icons": {
     *     "1x": "https://example.com/icon-128x128.png",
     *     "2x": "https://example.com/icon-256x256.png",
     *     "default": "https://example.com/icon-128x128.png"
     *   },
     *   "banners": {
     *     "low": "https://example.com/banner-772x250.jpg",
     *     "high": "https://example.com/banner-1544x500.jpg"
     *   },
     *   "changelog_items": ["Feature 1", "Bug fix", "Performance improvement"]
     * }
     * 
     * Minimum required fields: version, download_url
     * Optional fields: All others (will use defaults if not provided)
     */
    public function get_update_info() {
        $cache_key = 'st_update_info_cache';
        $cached_info = get_transient($cache_key);
        
        if ($cached_info !== false) {
            return $cached_info;
        }

        // Disable SSL verification for local/dev environments
        add_filter('https_ssl_verify', '__return_false');
        add_filter('https_local_ssl_verify', '__return_false');

        $response = wp_remote_get($this->update_url, array(
            'timeout' => 30,
            'sslverify' => false,
            'user-agent' => 'WordPress-Update-Checker/1.0',
            'headers' => array(
                'Accept' => 'application/json',
                'Cache-Control' => 'no-cache'
            )
        ));
        
        $status_key = 'st_update_endpoint_status';
        
        if (is_wp_error($response)) {
            $error_message = $response->get_error_message();
            $error_code = $response->get_error_code();
            $is_timeout = (strpos($error_message, 'timeout') !== false || 
                        strpos($error_message, 'timed out') !== false ||
                        $error_code === 'http_request_failed');
            
            set_transient($status_key, array(
                'status' => $is_timeout ? 'timeout' : 'error',
                'code' => 0,
                'message' => $error_message,
                'url' => $this->update_url,
                'checked' => current_time('mysql'),
                'error_code' => $error_code
            ), 1 * HOUR_IN_SECONDS);
            
            return false;
        }
        
        $response_code = isset($response['response']['code']) ? $response['response']['code'] : 0;
        
        if ($response_code === 200) {
            $update_info = json_decode($response['body'], true);
            
            if ($update_info && isset($update_info['version']) && isset($update_info['download_url'])) {
                set_transient($cache_key, $update_info, 6 * HOUR_IN_SECONDS);
                delete_transient($status_key);
            return $update_info;
            } else {
                set_transient($status_key, array(
                    'status' => 'invalid_json',
                    'code' => 200,
                    'message' => 'Endpoint returned invalid JSON response',
                    'url' => $this->update_url,
                    'checked' => current_time('mysql')
                ), 1 * HOUR_IN_SECONDS);
            }
        } elseif ($response_code === 404) {
            set_transient($status_key, array(
                'status' => 'not_found',
                'code' => 404,
                'message' => 'Update endpoint not found',
                'url' => $this->update_url,
                'checked' => current_time('mysql')
            ), 1 * HOUR_IN_SECONDS);
        } else {
            set_transient($status_key, array(
                'status' => 'http_error',
                'code' => $response_code,
                'message' => 'Update endpoint returned error code: ' . $response_code,
                'url' => $this->update_url,
                'checked' => current_time('mysql')
            ), 1 * HOUR_IN_SECONDS);
        }
        
        return false;
    }

    /**
     * Show minimal admin notice for endpoint issues
     * Only shows when WP_DEBUG is true and endpoint status is not 200
     */
    public function show_endpoint_notice() {
        // Only show in debug mode
        if (!defined('WP_DEBUG') || !WP_DEBUG) {
            return;
        }

        // Only show to admins
        if (!current_user_can('manage_options') && !current_user_can('update_plugins')) {
            return;
        }

        $status = get_transient('st_update_endpoint_status');
        
        if (!$status || !is_array($status)) {
            return; // No status tracked, assume OK
        }

        $status_type = isset($status['status']) ? $status['status'] : '';
        $code = isset($status['code']) ? $status['code'] : 0;

        // Only show if status is not 200 (success)
        if ($code === 200 && $status_type !== 'invalid_json') {
            return;
        }

        // Determine notice type
        $notice_type = ($status_type === 'error' || $status_type === 'timeout' || $status_type === 'invalid_json') ? 'error' : 'warning';
        $status_text = $code > 0 ? 'HTTP ' . $code : ucfirst(str_replace('_', ' ', $status_type));

        ?>
        <div class="notice notice-<?php echo esc_attr($notice_type); ?> is-dismissible">
            <p>
                <strong><?php echo esc_html($this->plugin_name); ?>:</strong> 
                Update endpoint status: <strong><?php echo esc_html($status_text); ?></strong>
            </p>
        </div>
        <?php
    }

    /**
     * Clear cache when database version changes
     */
    public function clear_cache_on_db_version_change() {
        delete_site_transient('update_plugins');
        delete_transient('st_update_info_cache');
        delete_transient('st_update_endpoint_status');
        
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log($this->plugin_name . ': Cache cleared due to database version change');
        }
    }
}
