<?php
/**
 * Updates Endpoint for BoostBot REST API.
 *
 * Provides update checking and installation functionality.
 *
 * @package BoostBot
 * @since 1.0.0
 */

// Prevent direct access to the file.
if (!defined('ABSPATH')) {
    exit;
}

/**
 * BoostBot_Updates_Endpoint class.
 *
 * Handles update checking and plugin update installation.
 *
 * @since 1.0.0
 */
class BoostBot_Updates_Endpoint extends BoostBot_Base_Endpoint
{
    /**
     * The base path for this endpoint.
     *
     * @var string
     */
    protected $rest_base = 'updates';

    /**
     * URL for remote version info.
     *
     * @var string
     */
    private $version_url = 'https://boostbot.boostmedia.co.il/downloads/boostbot/version.json';

    /**
     * Register the routes for this endpoint.
     */
    public function register()
    {
        $this->register_route(
            '/check',
            'GET',
            [$this, 'check_for_updates'],
            [$this, 'admin_permission_check']
        );

        $this->register_route(
            '/install',
            'POST',
            [$this, 'install_update'],
            [$this, 'admin_permission_check']
        );

        $this->register_route(
            '/auto-update',
            'GET',
            [$this, 'get_auto_update_setting'],
            [$this, 'admin_permission_check']
        );

        $this->register_route(
            '/auto-update',
            'POST',
            [$this, 'set_auto_update_setting'],
            [$this, 'admin_permission_check']
        );
    }

    /**
     * Permission check: must be an admin.
     *
     * @param \WP_REST_Request $request
     * @return bool
     */
    public function admin_permission_check($request)
    {
        return current_user_can('manage_options');
    }

    /**
     * Check for available updates.
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function check_for_updates($request)
    {
        $current_version = defined('BOOSTBOT_VERSION') ? BOOSTBOT_VERSION : '1.0.0';

        // Fetch remote version info
        $remote = $this->fetch_remote_version();

        if (is_wp_error($remote)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => $remote->get_error_message(),
            ], 500);
        }

        $update_available = version_compare($remote['version'], $current_version, '>');

        // Store last check time
        update_option('boostbot_last_update_check', current_time('mysql'));
        update_option('boostbot_latest_version_info', $remote);

        return new \WP_REST_Response([
            'success' => true,
            'data' => [
                'current_version'  => $current_version,
                'latest_version'   => $remote['version'],
                'update_available' => $update_available,
                'download_url'     => $remote['download_url'] ?? '',
                'changelog'        => $remote['changelog'] ?? '',
                'release_date'     => $remote['release_date'] ?? '',
                'last_checked'     => current_time('c'),
            ],
        ], 200);
    }

    /**
     * Install an update.
     *
     * Uses Plugin_Upgrader::upgrade() for proper in-place updates.
     * IMPORTANT: Do NOT use Plugin_Upgrader::install() for updates — it is
     * designed for fresh installations and can create duplicate plugin
     * directories when the overwrite fails.
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function install_update($request)
    {
        $download_url = $request->get_param('download_url');

        if (empty($download_url)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => 'No download URL provided.',
            ], 400);
        }

        // Validate URL domain
        $parsed = wp_parse_url($download_url);
        $allowed_hosts = ['boostbot.boostmedia.co.il', 'boostmedia.co.il'];
        if (!isset($parsed['host']) || !in_array($parsed['host'], $allowed_hosts, true)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => 'Invalid download URL.',
            ], 400);
        }

        // Use WordPress built-in upgrader
        require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
        require_once ABSPATH . 'wp-admin/includes/plugin.php';
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/misc.php';

        // Inject the download URL into the update transient so the upgrader
        // knows where to download from when upgrade() is called.
        $plugin_file = 'boostbot/boostbot.php';

        $update_data = (object) array(
            'slug'        => 'boostbot',
            'plugin'      => $plugin_file,
            'new_version' => $request->get_param('version') ?? BOOSTBOT_VERSION,
            'package'     => $download_url,
        );

        $transient = get_site_transient('update_plugins');
        if (! is_object($transient)) {
            $transient = new \stdClass();
            $transient->response = array();
        }
        $transient->response[$plugin_file] = $update_data;
        set_site_transient('update_plugins', $transient);

        // Use a skin that captures output instead of printing
        $skin     = new \WP_Ajax_Upgrader_Skin();
        $upgrader = new \Plugin_Upgrader($skin);

        // Use upgrade() — the correct method for updating an existing plugin.
        // This safely backs up, replaces, and verifies the plugin directory.
        $result = $upgrader->upgrade($plugin_file);

        if (is_wp_error($result)) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => $result->get_error_message(),
            ], 500);
        }

        if ($result === false) {
            $errors = $skin->get_errors();
            $message = 'Update failed.';
            if (is_wp_error($errors) && $errors->has_errors()) {
                $message = $errors->get_error_message();
            }
            return new \WP_REST_Response([
                'success' => false,
                'message' => $message,
            ], 500);
        }

        // Re-activate the plugin after update (upgrade deactivates it)
        if (!is_plugin_active($plugin_file)) {
            $activated = activate_plugin($plugin_file);
            if (is_wp_error($activated)) {
                return new \WP_REST_Response([
                    'success' => true,
                    'message' => 'Plugin updated but activation failed: ' . $activated->get_error_message(),
                ], 200);
            }
        }

        return new \WP_REST_Response([
            'success' => true,
            'message' => 'Plugin updated successfully.',
        ], 200);
    }

    /**
     * Fetch version info from remote server.
     *
     * @return array|\WP_Error
     */
    private function fetch_remote_version()
    {
        $response = wp_remote_get($this->version_url, [
            'timeout' => 15,
            'headers' => [
                'Accept' => 'application/json',
            ],
        ]);

        if (is_wp_error($response)) {
            return $response;
        }

        $code = wp_remote_retrieve_response_code($response);
        if ($code !== 200) {
            return new \WP_Error(
                'update_check_failed',
                sprintf('Failed to check for updates (HTTP %d).', $code)
            );
        }

        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (!$data || !isset($data['version'])) {
            return new \WP_Error(
                'invalid_response',
                'Invalid response from update server.'
            );
        }

        return $data;
    }

    /**
     * Get auto-update setting.
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function get_auto_update_setting($request)
    {
        // Use 'yes'/'no' strings to avoid the WordPress update_option(false) bug:
        // when the option doesn't exist, get_option() returns false internally,
        // so update_option('key', false) thinks nothing changed and never saves.
        $raw = get_option('boostbot_auto_update', 'yes');
        $enabled = ($raw === 'yes');

        return new \WP_REST_Response([
            'success' => true,
            'data'    => [
                'auto_update' => $enabled,
            ],
        ], 200);
    }

    /**
     * Set auto-update setting.
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function set_auto_update_setting($request)
    {
        $enabled = $request->get_param('auto_update');

        if ($enabled === null) {
            return new \WP_REST_Response([
                'success' => false,
                'message' => 'Missing auto_update parameter.',
            ], 400);
        }

        $value = $enabled ? 'yes' : 'no';
        update_option('boostbot_auto_update', $value);

        return new \WP_REST_Response([
            'success' => true,
            'data'    => [
                'auto_update' => ($value === 'yes'),
            ],
        ], 200);
    }
}
