<?php
/**
 * Theme Update Executor - Runs WordPress theme upgrade logic (WordPress.org / installed themes).
 *
 * @package FleekDash\Controllers\Plugins
 * @since 2.2.0
 */

namespace FleekDash\Controllers\Plugins;

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

/**
 * Single installed theme update via Theme_Upgrader (same flow as update-core / plugin executor).
 */
class ThemeUpdateExecutor
{
    /** @var WordPressUpdateLoader */
    private $wpLoader;

    public function __construct(?WordPressUpdateLoader $wpLoader = null)
    {
        $this->wpLoader = $wpLoader ?? new WordPressUpdateLoader();
    }

    /**
     * Resolve stylesheet slug from JSON body: themes[] (WP update form) or stylesheet.
     */
    public function resolveThemeStylesheetParam(\WP_REST_Request $request): ?string
    {
        $params = $request->get_json_params();
        if (!\is_array($params)) {
            $params = [];
        }
        $stylesheet = $request->get_param('stylesheet');
        if (!empty($stylesheet) && \is_string($stylesheet)) {
            return \sanitize_text_field($stylesheet);
        }
        $themes = $params['themes'] ?? $request->get_param('themes');
        if (\is_array($themes) && isset($themes[0]) && \is_string($themes[0]) && $themes[0] !== '') {
            return \sanitize_text_field($themes[0]);
        }

        return null;
    }

    public function loadWordPressDependencies(): void
    {
        $this->wpLoader->loadThemeUpgrade();
    }

    /**
     * @return array{status: string, name?: string, message?: string, old_version?: string, new_version?: string}
     */
    public function runUpdate(string $stylesheet): array
    {
        $stylesheet = \sanitize_text_field($stylesheet);
        $theme = \wp_get_theme($stylesheet);
        $result = $this->validateForUpdate($stylesheet, $theme);
        if ($result !== null) {
            return $result;
        }

        $theme_name = $theme->get('Name') ?: $stylesheet;
        $old_version = $theme->get('Version') ?: 'unknown';
        $current = $this->resolveUpdateThemesTransient($stylesheet);

        return $this->executeUpgrade($stylesheet, $theme_name, $old_version, $current);
    }

    /**
     * @return \WP_REST_Response
     */
    public function buildSuccessResponse(array $result, string $stylesheet)
    {
        $name = $result['name'] ?? $stylesheet;
        $status = $result['status'];
        $response = null;

        if ($status === 'updated') {
            $response = \rest_ensure_response([
                'success' => true,
                'message' => \sprintf('Theme "%s" updated successfully', $name),
                'updated_themes' => [[
                    'name' => $name,
                    'old_version' => $result['old_version'] ?? '',
                    'new_version' => $result['new_version'] ?? '',
                ]],
            ]);
        } elseif ($status === 'already_up_to_date') {
            $response = \rest_ensure_response([
                'success' => true,
                'already_up_to_date' => true,
                'message' => $result['message'] ?? \sprintf('Theme "%s" is already up to date', $name),
            ]);
        } elseif ($status === 'license_required') {
            $response = \rest_ensure_response([
                'success' => false,
                'license_required' => true,
                'message' => $result['message'] ?? \sprintf('Theme "%s" requires a valid license to update', $name),
                'themes' => [[
                    'name' => $name,
                    'current_version' => $result['current_version'] ?? '',
                    'new_version' => $result['new_version'] ?? '',
                ]],
            ]);
            $response->set_status(403);
        } else {
            $response = \rest_ensure_response([
                'success' => false,
                'message' => $result['message'] ?? 'Theme update failed',
                'failed_themes' => [[
                    'name' => $name,
                    'message' => $result['message'] ?? 'Update failed',
                ]],
            ]);
            // not_found → 404; failed (and any other non-success status in this branch) → 5xx so clients can use HTTP status.
            $response->set_status($status === 'not_found' ? 404 : 500);
        }

        return $response;
    }

    /**
     * @return \WP_REST_Response
     */
    public function buildErrorResponse(\Throwable $e, ?string $stylesheet)
    {
        $stylesheet = $stylesheet ?? 'unknown';
        $theme = \wp_get_theme($stylesheet);
        $theme_name = $this->resolveInstalledThemeLabel($theme, $stylesheet);

        fleekdash_log_error(
            \sprintf(
                'Theme update error for %s: %s in %s:%d',
                $stylesheet,
                $e->getMessage(),
                $e->getFile(),
                $e->getLine()
            )
        );

        $resp = \rest_ensure_response([
            'success' => false,
            'message' => \sprintf('Failed to update %s: %s', $theme_name, $e->getMessage()),
            'error' => $e->getMessage(),
        ]);
        $resp->set_status(500);

        return $resp;
    }

    /**
     * @return object|false
     */
    private function resolveUpdateThemesTransient(string $stylesheet)
    {
        $current = \get_site_transient('update_themes');
        $has_entry = \is_object($current)
            && isset($current->response)
            && \is_array($current->response)
            && isset($current->response[$stylesheet]);

        if (!$has_entry && \function_exists('wp_update_themes')) {
            \wp_update_themes();
            $current = \get_site_transient('update_themes');
        }

        return $current;
    }

    /**
     * @param \WP_Theme $theme
     * @return array<string, mixed>|null
     */
    private function validateForUpdate(string $stylesheet, $theme): ?array
    {
        if (!$theme->exists()) {
            return [
                'status' => 'not_found',
                'name' => $stylesheet,
                'message' => 'Theme not found or not installed',
            ];
        }

        return null;
    }

    /**
     * @param object|null $current update_themes transient
     * @return array{status: string, name?: string, message?: string, old_version?: string, new_version?: string}
     */
    private function executeUpgrade(string $stylesheet, string $theme_name, string $old_version, $current): array
    {
        $skin = new PluginUpgraderSkin();
        $upgrader = new \Theme_Upgrader($skin);

        try {
            $upgrade_result = $upgrader->upgrade($stylesheet, ['clear_update_cache' => false]);
        } catch (\Throwable $e) {
            fleekdash_log_error('FleekDash: Theme update threw for ' . $stylesheet . ': ' . $e->getMessage());

            return [
                'status' => 'failed',
                'name' => $theme_name,
                'message' => $e->getMessage(),
            ];
        }

        $early = $this->handleUpgradeResult($upgrade_result, $skin, $stylesheet, $theme_name, $old_version, $current);
        if ($early !== null) {
            return $early;
        }

        $theme_after = \wp_get_theme($stylesheet);
        $new_version = $this->resolveThemeVersionAfterUpgrade($theme_after, $old_version);

        return [
            'status' => 'updated',
            'name' => $theme_name,
            'old_version' => $old_version,
            'new_version' => $new_version,
        ];
    }

    /**
     * @param bool|\WP_Error $upgrade_result
     * @param object|null $current
     * @return array<string, mixed>|null
     */
    private function handleUpgradeResult($upgrade_result, PluginUpgraderSkin $skin, string $stylesheet, string $theme_name, string $old_version, $current): ?array
    {
        $out = null;

        if (\is_wp_error($upgrade_result)) {
            $out = $this->buildUpgradeErrorResult(
                $skin,
                $upgrade_result->get_error_message(),
                $stylesheet,
                $theme_name,
                $old_version,
                $current
            );
        } elseif ($upgrade_result === false) {
            if (!$skin->has_errors()) {
                $out = [
                    'status' => 'already_up_to_date',
                    'name' => $theme_name,
                    'message' => \sprintf('Theme "%s" is already up to date (version %s)', $theme_name, $old_version),
                    'version' => $old_version,
                ];
            } else {
                $out = $this->buildUpgradeErrorResult(
                    $skin,
                    $skin->get_first_error() ?? 'Update process returned false',
                    $stylesheet,
                    $theme_name,
                    $old_version,
                    $current
                );
            }
        } elseif ($skin->has_errors() && $skin->is_license_error()) {
            $out = $this->buildLicenseRequiredResult(
                $skin->get_first_error() ?? $skin->get_all_error_messages(),
                $stylesheet,
                $theme_name,
                $old_version,
                $current
            );
        }

        return $out;
    }

    /**
     * @param \WP_Theme $theme
     */
    private function resolveInstalledThemeLabel($theme, string $stylesheet): string
    {
        if (!$theme->exists()) {
            return $stylesheet;
        }
        $name = $theme->get('Name');
        if (!empty($name)) {
            return (string) $name;
        }

        return $stylesheet;
    }

    /**
     * @param \WP_Theme $theme_after
     */
    private function resolveThemeVersionAfterUpgrade($theme_after, string $old_version): string
    {
        if (!$theme_after->exists()) {
            return $old_version;
        }
        $version = $theme_after->get('Version');
        if (!empty($version)) {
            return (string) $version;
        }

        return $old_version;
    }

    /**
     * @param object|null $current
     * @return array{status: string, name: string, message: string}
     */
    private function buildUpgradeErrorResult(
        PluginUpgraderSkin $skin,
        string $error_message,
        string $stylesheet,
        string $theme_name,
        string $old_version,
        $current
    ): array {
        if ($skin->has_errors()) {
            $skin_err = $skin->get_first_error() ?? $skin->get_all_error_messages();
            if (!empty($skin_err) && \strlen($skin_err) > \strlen($error_message)) {
                $error_message = $skin_err;
            }
        }

        $is_license = $skin->is_license_error() || preg_match('/license|licence|activation|premium|subscription|key/i', $error_message);
        if ($is_license) {
            return $this->buildLicenseRequiredResult($error_message, $stylesheet, $theme_name, $old_version, $current);
        }

        return [
            'status' => 'failed',
            'name' => $theme_name,
            'message' => $error_message,
        ];
    }

    /**
     * @param object|null $current
     * @return array{status: string, name: string, message: string, current_version: string, new_version: string}
     */
    private function buildLicenseRequiredResult(
        string $message,
        string $stylesheet,
        string $theme_name,
        string $old_version,
        $current
    ): array {
        $update_info = (\is_object($current) && isset($current->response[$stylesheet]))
            ? $current->response[$stylesheet]
            : null;
        $new_version = '';
        if (\is_array($update_info) && isset($update_info['new_version'])) {
            $new_version = (string) $update_info['new_version'];
        } elseif (\is_object($update_info) && isset($update_info->new_version)) {
            $new_version = (string) $update_info->new_version;
        }

        return [
            'status' => 'license_required',
            'name' => $theme_name,
            'message' => $message,
            'current_version' => $old_version,
            'new_version' => $new_version,
        ];
    }
}
