<?php

namespace FleekDash\API;

use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
use FleekDash\Core\AuthMiddleware;

// Load debug helpers
if (!function_exists('fleekdash_log_debug')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}

/**
 * Update Management API
 *
 * Unified endpoint for checking and managing WordPress updates
 * (Core, Plugins, Themes, Translations) with async job support
 *
 * @package FleekDash\API
 * @SuppressWarnings(PHPMD.CamelCaseMethodName) REST callbacks use snake_case per WordPress conventions
 */
class UpdateManagementAPI
{
    /**
     * Cache duration for update checks
     */
    const CACHE_DURATION = 3600; // 1 hour

    private const MSG_ADMIN_REQUIRED = 'Admin access required';
    private const PATH_UPDATE_PHP = 'wp-admin/includes/update.php';
    private const PATH_INCLUDES_UPDATE_PHP = 'wp-includes/update.php';

    /**
     * Constructor
     *
     * Migrated (v2.2): PluginsRoutes (Phase 2)
     * Routes registered via UpdatesController / PluginsRoutes
     */
    public function __construct()
    {
    }

    /**
     * Register REST API routes
     *
     * @phpstan-ignore sonarqube.php.S100 (REST callback contract - UpdatesController/PluginsRoutes)
     */
    public function register_routes() // NOSONAR php:S100
    {
        $namespace = 'fleekdash/v1';

        // Get all available updates
        \register_rest_route($namespace, '/updates/check', array(
            'methods' => 'GET',
            'callback' => array($this, 'checkUpdates'),
            'permission_callback' => array($this, 'check_permissions'),
        ));

        // Force check for updates (clears cache)
        \register_rest_route($namespace, '/updates/check/force', array(
            'methods' => 'POST',
            'callback' => array($this, 'forceCheckUpdates'),
            'permission_callback' => array($this, 'check_permissions'),
        ));

        // Start async update
        \register_rest_route($namespace, '/updates/start', array(
            'methods' => 'POST',
            'callback' => array($this, 'startUpdate'),
            'permission_callback' => array($this, 'check_permissions'),
            'args' => array(
                'type' => array('required' => true, 'type' => 'string', 'enum' => array('core', 'plugin', 'theme', 'translation')),
                'item' => array('required' => false, 'type' => 'string'),
            ),
        ));

        // Get update job status
        \register_rest_route($namespace, '/updates/status/(?P<job_id>[a-zA-Z0-9_-]+)', array(
            'methods' => 'GET',
            'callback' => array($this, 'getUpdateStatus'),
            'permission_callback' => array($this, 'check_permissions'),
        ));

        // Cancel update
        \register_rest_route($namespace, '/updates/cancel/(?P<job_id>[a-zA-Z0-9_-]+)', array(
            'methods' => 'DELETE',
            'callback' => array($this, 'cancelUpdate'),
            'permission_callback' => array($this, 'check_permissions'),
        ));

        // Bulk updates
        \register_rest_route($namespace, '/updates/bulk', array(
            'methods' => 'POST',
            'callback' => array($this, 'bulkUpdate'),
            'permission_callback' => array($this, 'check_permissions'),
            'args' => array(
                'items' => array('required' => true, 'type' => 'array'),
            ),
        ));
    }

    /**
     * Check permissions
     *
     * @phpstan-ignore sonarqube.php.S100 (REST permission callback contract)
     */
    public function check_permissions() // NOSONAR php:S100
    {
        return true; // Let AuthMiddleware handle auth
    }

    /**
     * Check for all available updates
     */
    public function checkUpdates(WP_REST_Request $request)
    {
        try {
            $auth_check = AuthMiddleware::check('authenticated');
            if (!$auth_check['authenticated']) {
                fleekdash_log_debug('FleekDash: Updates check without auth: ' . $auth_check['reason'] . ' (method: ' . $request->get_method() . ')');
            }

            // Check cache
            $cache_key = 'fleekdash_all_updates';
            $cached = \get_transient($cache_key);

            if ($cached !== false) {
                return \rest_ensure_response($cached);
            }

            // Get all updates
            $updates = array(
                'core' => $this->get_core_updates(),
                'plugins' => $this->get_plugin_updates(),
                'themes' => $this->get_theme_updates(),
                'translations' => $this->get_translation_updates(),
            );

            // Count totals
            $total = 0;
            $total += isset($updates['core']['available']) && $updates['core']['available'] ? 1 : 0;
            $total += count($updates['plugins']);
            $total += count($updates['themes']);
            $total += count($updates['translations']);

            $response_data = array(
                'success' => true,
                'updates' => $updates,
                'summary' => array(
                    'total' => $total,
                    'core' => isset($updates['core']['available']) && $updates['core']['available'] ? 1 : 0,
                    'plugins' => count($updates['plugins']),
                    'themes' => count($updates['themes']),
                    'translations' => count($updates['translations']),
                ),
                'last_checked' => \current_time('mysql'),
            );

            // Cache for 1 hour
            \set_transient($cache_key, $response_data, self::CACHE_DURATION);

            return \rest_ensure_response($response_data);

        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Updates check error: ' . $e->getMessage());
            return new WP_Error('updates_error', 'Error checking updates', array('status' => 500));
        }
    }

    /**
     * Force check for updates (clears cache)
     *
     * @phpstan-ignore sonarqube.php.S100 (REST callback contract)
     */
    public function forceCheckUpdates(WP_REST_Request $request) // NOSONAR php:S100
    {
        try {
            $auth_check = AuthMiddleware::check('admin');
            if (!$auth_check['authenticated']) {
                return new WP_Error('unauthorized', self::MSG_ADMIN_REQUIRED, array('status' => 403));
            }

            // Load required files
            if (!function_exists('wp_version_check')) {
                require_once ABSPATH . self::PATH_UPDATE_PHP;
            }
            if (!function_exists('wp_update_plugins')) {
                require_once ABSPATH . self::PATH_INCLUDES_UPDATE_PHP;
            }

            // Clear all update caches (WordPress)
            \delete_transient('fleekdash_all_updates');
            \delete_site_transient('update_core');
            \delete_site_transient('update_plugins');
            \delete_site_transient('update_themes');
            \delete_site_transient('update_translations');

            // Force FleekDash UpdateChecker to check for updates
            // This clears the FleekDash-specific cache and checks Supabase
            if (class_exists('FleekDash\\Core\\UpdateChecker')) {
                $update_checker_file = FLEEKDASH_PLUGIN_DIR . 'includes/Core/UpdateChecker.php';
                if (file_exists($update_checker_file)) {
                    require_once $update_checker_file;
                    if (!\function_exists('get_plugin_data')) {
                        require_once ABSPATH . 'wp-admin/includes/plugin.php';
                    }
                    $plugin_file = 'fleekdash/fleekdash.php';
                    $current_version = \get_plugin_data(FLEEKDASH_PLUGIN_FILE, false, false)['Version'] ?? '2.1.0';
                    $update_checker = new \FleekDash\Core\UpdateChecker($plugin_file, $current_version);
                    $update_checker->forceCheck();
                    fleekdash_log('UpdateChecker: Force check executed via API', 'info');
                }
            }

            // Force WordPress to check for updates
            \wp_version_check(array(), true);
            \wp_update_plugins();
            \wp_update_themes();
            \wp_get_translation_updates();

            // Get fresh data
            return $this->checkUpdates($request);

        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Force check error: ' . $e->getMessage());
            return new WP_Error('force_check_error', 'Error forcing update check', array('status' => 500));
        }
    }

    /**
     * Start async update
     *
     * @phpstan-ignore sonarqube.php.S100 (REST callback contract)
     */
    public function startUpdate(WP_REST_Request $request) // NOSONAR php:S100
    {
        try {
            $auth_check = AuthMiddleware::check('admin');
            if (!$auth_check['authenticated']) {
                return new WP_Error('unauthorized', self::MSG_ADMIN_REQUIRED, array('status' => 403));
            }

            $type = $request->get_param('type');
            $item = $request->get_param('item');

            // Generate job ID
            $job_id = 'update_' . $type . '_' . ($item ? $item : 'core') . '_' . time();

            // Store initial job status
            $job_status = array(
                'state' => 'pending',
                'progress' => 0,
                'step' => 'Preparing to update ' . $type . '...',
                'started_at' => \current_time('mysql'),
                'logs' => array(
                    '[' . \current_time('mysql') . '] Update job created',
                ),
                'type' => $type,
                'item' => $item,
            );

            \set_transient('fleekdash_update_job_' . $job_id, $job_status, 3600);

            // Schedule async update
            \wp_schedule_single_event(time(), 'fleekdash_process_update', array($job_id));

            return \rest_ensure_response(array(
                'success' => true,
                'job_id' => $job_id,
                'message' => 'Update started',
                'status' => $job_status,
            ));

        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Start update error: ' . $e->getMessage());
            return new WP_Error('startUpdate_error', 'Error starting update', array('status' => 500));
        }
    }

    /**
     * Get update job status
     *
     * @phpstan-ignore sonarqube.php.S100 (REST callback contract)
     */
    public function getUpdateStatus(WP_REST_Request $request) // NOSONAR php:S100
    {
        try {
            $auth_check = AuthMiddleware::check('authenticated');
            if (!$auth_check['authenticated']) {
                fleekdash_log_debug('FleekDash: Update status check without auth');
            }

            $job_id = $request->get_param('job_id');

            $job_status = \get_transient('fleekdash_update_job_' . $job_id);

            if ($job_status === false) {
                return new WP_Error('job_not_found', 'Update job not found', array('status' => 404));
            }

            return \rest_ensure_response(array(
                'success' => true,
                'job_id' => $job_id,
                'status' => $job_status,
            ));

        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Get status error: ' . $e->getMessage());
            return new WP_Error('status_error', 'Error getting status', array('status' => 500));
        }
    }

    /**
     * Cancel update
     *
     * @phpstan-ignore sonarqube.php.S100 (REST callback contract)
     */
    public function cancelUpdate(WP_REST_Request $request) // NOSONAR php:S100
    {
        try {
            $auth_check = AuthMiddleware::check('admin');
            if (!$auth_check['authenticated']) {
                return new WP_Error('unauthorized', self::MSG_ADMIN_REQUIRED, array('status' => 403));
            }

            $job_id = $request->get_param('job_id');

            // Delete the job transient
            \delete_transient('fleekdash_update_job_' . $job_id);

            return \rest_ensure_response(array(
                'success' => true,
                'message' => 'Update cancelled',
            ));

        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Cancel update error: ' . $e->getMessage());
            return new WP_Error('cancel_error', 'Error cancelling update', array('status' => 500));
        }
    }

    /**
     * Bulk updates
     *
     * @phpstan-ignore sonarqube.php.S100 (REST callback contract)
     */
    public function bulkUpdate(WP_REST_Request $request) // NOSONAR php:S100
    {
        try {
            $auth_check = AuthMiddleware::check('admin');
            if (!$auth_check['authenticated']) {
                return new WP_Error('unauthorized', self::MSG_ADMIN_REQUIRED, array('status' => 403));
            }

            $items = $request->get_param('items');

            $results = array();

            foreach ($items as $item_data) {
                $sub_request = new WP_REST_Request('POST', '/fleekdash/v1/updates/start');
                $sub_request->set_param('type', $item_data['type']);
                if (isset($item_data['item'])) {
                    $sub_request->set_param('item', $item_data['item']);
                }

                $response = $this->startUpdate($sub_request);
                $data = $response->get_data();

                $results[] = array(
                    'type' => $item_data['type'],
                    'item' => $item_data['item'] ?? null,
                    'success' => isset($data['success']) && $data['success'],
                    'job_id' => $data['job_id'] ?? null,
                );
            }

            return \rest_ensure_response(array(
                'success' => true,
                'results' => $results,
            ));

        } catch (\Exception $e) {
            fleekdash_log_error('FleekDash: Bulk update error: ' . $e->getMessage());
            return new WP_Error('bulk_error', 'Error performing bulk update', array('status' => 500));
        }
    }

    /**
     * Get WordPress Core updates
     *
     * @phpstan-ignore sonarqube.php.S100 (Internal helper - naming consistency)
     */
    private function get_core_updates() // NOSONAR php:S100
    {
        if (!function_exists('get_core_updates')) {
            require_once ABSPATH . self::PATH_UPDATE_PHP;
        }

        \wp_version_check();
        $updates = \get_core_updates();

        if (empty($updates) || !is_array($updates)) {
            return array('available' => false);
        }

        $update = $updates[0];

        if ($update->response === 'latest') {
            return array('available' => false);
        }

        return array(
            'available' => true,
            'current_version' => \get_bloginfo('version'),
            'new_version' => $update->current,
            'package' => $update->package ?? '',
            'locale' => $update->locale ?? \get_locale(),
            'response' => $update->response,
        );
    }

    /**
     * Get plugin updates
     *
     * @phpstan-ignore sonarqube.php.S100 (Internal helper - naming consistency)
     */
    private function get_plugin_updates() // NOSONAR php:S100
    {
        if (!\function_exists('get_plugins')) {
            \FleekDash\Core\PluginHelper::ensurePluginApiLoaded();
        }

        if (!function_exists('wp_update_plugins')) {
            require_once ABSPATH . self::PATH_INCLUDES_UPDATE_PHP;
        }

        \wp_update_plugins();
        $update_plugins = \get_site_transient('update_plugins');
        // Prefix with global namespace to match other WordPress functions in this file
        $all_plugins = \get_plugins();

        $updates = array();

        if (empty($update_plugins->response)) {
            return $updates;
        }

        foreach ($update_plugins->response as $plugin_file => $plugin_data) {
            if (!isset($all_plugins[$plugin_file])) {
                continue;
            }

            $updates[] = array(
                'file' => $plugin_file,
                'slug' => dirname($plugin_file) === '.' ? basename($plugin_file, '.php') : dirname($plugin_file),
                'name' => $all_plugins[$plugin_file]['Name'],
                'current_version' => $all_plugins[$plugin_file]['Version'],
                'new_version' => $plugin_data->new_version,
                'package' => $plugin_data->package ?? '',
                'tested' => $plugin_data->tested ?? '',
                'requires_php' => $plugin_data->requires_php ?? '',
            );
        }

        return $updates;
    }

    /**
     * Get theme updates
     *
     * @phpstan-ignore sonarqube.php.S100 (Internal helper - naming consistency)
     */
    private function get_theme_updates() // NOSONAR php:S100
    {
        if (!function_exists('wp_update_themes')) {
            require_once ABSPATH . self::PATH_INCLUDES_UPDATE_PHP;
        }

        \wp_update_themes();
        $update_themes = \get_site_transient('update_themes');

        $updates = array();

        if (empty($update_themes->response)) {
            return $updates;
        }

        foreach ($update_themes->response as $theme_slug => $theme_data) {
            $theme = wp_get_theme($theme_slug);

            // wp_get_theme() always returns WP_Theme object (never null)
            // Only need to check exists() - the !$theme check is redundant
            if (!$theme->exists()) {
                continue;
            }

            $updates[] = array(
                'slug' => $theme_slug,
                'name' => $theme->get('Name'),
                'current_version' => $theme->get('Version'),
                'new_version' => $theme_data['new_version'] ?? '',
                'package' => $theme_data['package'] ?? '',
            );
        }

        return $updates;
    }

    /**
     * Get translation updates
     *
     * @phpstan-ignore sonarqube.php.S100 (Internal helper - naming consistency)
     */
    private function get_translation_updates() // NOSONAR php:S100
    {
        if (!function_exists('wp_get_translation_updates')) {
            require_once ABSPATH . self::PATH_UPDATE_PHP;
        }

        $translation_updates = \wp_get_translation_updates();

        $updates = array();

        if (empty($translation_updates)) {
            return $updates;
        }

        foreach ($translation_updates as $translation) {
            $updates[] = array(
                'type' => $translation->type ?? 'core',
                'slug' => $translation->slug ?? '',
                'language' => $translation->language ?? '',
                'version' => $translation->version ?? '',
                'package' => $translation->package ?? '',
            );
        }

        return $updates;
    }
}
