<?php
// +----------------------------------------------------------------------
// | onezan-admin 快速开发的管理平台
// +----------------------------------------------------------------------
// | 官方网址：https://www.onezan.cn
// +----------------------------------------------------------------------
// | onezan团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: 543885307@qq.com
// +----------------------------------------------------------------------
namespace app\service\domain\upgrade\handler;

use Core\service\queue\contract\QueueHandlerInterface;
use app\model\upgrade\UpgradeLog;
use app\service\domain\upgrade\UpgradeBackupService;
use app\service\domain\upgrade\UpgradeRuntimeRefreshService;
use think\facade\Log;

/**
 * 升级回滚处理程序
 * @package app\service\domain\upgrade\handler
 */
class UpgradeRollbackHandler implements QueueHandlerInterface
{
    /**
     * 获取处理程序订阅的队列主题
     * @return string
     */
    public function topic(): string
    {
        return 'upgrade.rollback';
    }

    /**
     * 处理升级回滚任务
     * @param array $payload 任务负载
     * @return void
     */
    public function handle(array $payload): void
    {
        $rollbackLogId = (int)($payload['rollback_log_id'] ?? 0);
        $sourceLogId   = (int)($payload['source_log_id']   ?? 0);
        $type          = (string)($payload['type']          ?? 'framework');
        $fromVersion   = (string)($payload['from_version']  ?? '');
        $appCode  = (string)($payload['industry_code'] ?? '');
        $backupPath    = (string)($payload['backup_path']   ?? '');

        $onProgress = function (int $percent, string $message) use ($rollbackLogId) {
            UpgradeLog::where('id', $rollbackLogId)->update([
                'progress' => $percent,
                'message'  => $message,
            ]);
        };

        try {
            $onProgress(10, 'Validating backup files...');

            if (empty($backupPath) || !is_dir($backupPath)) {
                throw new \RuntimeException('Backup path not found: ' . ($backupPath ?: '(empty)'));
            }

            UpgradeLog::where('id', $rollbackLogId)->update([
                'status'   => 2,
                'progress' => 20,
                'message'  => 'Restoring backup files...',
            ]);

            $onProgress(30, 'Restoring backup files...');

            /** @var UpgradeBackupService $backupService */
            $backupService = app()->make(UpgradeBackupService::class);
            $backupService->restore($backupPath);

            $onProgress(70, 'Reverting version...');

            // Revert manifest.json version
            if ($type === 'framework') {
                $manifestFile = app()->getRootPath() . 'core/manifest/manifest.json';
            } else {
                $manifestFile = app()->getRootPath() . 'applications/' . $appCode . '/manifest.json';
            }

            if (file_exists($manifestFile)) {
                $manifest = json_decode(file_get_contents($manifestFile), true);
                if (is_array($manifest)) {
                    $manifest['version'] = $fromVersion;
                    file_put_contents($manifestFile, json_encode($manifest, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
                }
            }

            // Clear opcache
            if (function_exists('opcache_reset')) {
                opcache_reset();
            }

            $refresh = app()->make(UpgradeRuntimeRefreshService::class)->refresh('rollback-success');
            if ((int)($refresh['code'] ?? 1) !== 0) {
                throw new \RuntimeException('Rollback complete but platform sync failed: ' . (string)($refresh['msg'] ?? 'unknown error'));
            }

            // Mark source upgrade log as rolled back
            if ($sourceLogId > 0) {
                UpgradeLog::where('id', $sourceLogId)->update([
                    'status'      => 6,
                    'progress'    => 100,
                    'message'     => 'Rolled back to ' . $fromVersion,
                    'finished_at' => date('Y-m-d H:i:s'),
                ]);
            }

            // Rollback complete
            UpgradeLog::where('id', $rollbackLogId)->update([
                'status'      => 3,
                'progress'    => 100,
                'message'     => 'Rollback complete',
                'from_version' => $fromVersion,
                'finished_at'  => date('Y-m-d H:i:s'),
            ]);
        } catch (\Throwable $e) {
            Log::error('upgrade rollback failed: ' . $e->getMessage() . "\n" . $e->getTraceAsString());

            UpgradeLog::where('id', $rollbackLogId)->update([
                'status'       => 4,
                'progress'     => 0,
                'message'      => 'Rollback failed',
                'error_detail' => $e->getMessage(),
                'finished_at'  => date('Y-m-d H:i:s'),
            ]);
        }
    }
}
