<?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\UpgradeDownloader;
use app\service\domain\upgrade\UpgradeInstaller;
use app\service\domain\upgrade\UpgradeRuntimeRefreshService;
use think\facade\Log;

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

    /**
     * 处理升级执行任务
     * @param array $payload 任务负载
     * @return void
     */
    public function handle(array $payload): void
    {
        $logId        = (int)($payload['log_id']         ?? 0);
        $type         = (string)($payload['type']         ?? 'framework');
        $version      = (string)($payload['version']      ?? '');
        $appCode = (string)($payload['industry_code'] ?? '');
        $code         = (string)($payload['code']         ?? '');
        $updateFrontend = (bool)($payload['update_frontend'] ?? true);

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

        try {
            $downloader = app()->make(UpgradeDownloader::class);
            $onProgress(10, 'Downloading package...');
            $download = $downloader->download($type, $version, $appCode, $code);

            UpgradeLog::where('id', $logId)->update([
                'status'       => 1,
                'progress'     => 20,
                'message'      => 'Download complete, ready to install',
                'download_url' => '',
                'package_hash' => $download['hash'],
            ]);

            $installer = app()->make(UpgradeInstaller::class);
            $onProgress(25, 'Installing...');

            // Wrap install to capture backup_path before exception unwinds
            $report = null;
            try {
                $report = $installer->install(
                    $download['file_path'],
                    $type,
                    $version,
                    $appCode,
                    $onProgress,
                    $updateFrontend
                );
            } catch (\Throwable $installError) {
                // Save backup_path immediately so rollback can find it
                if ($report && !empty($report['backup_path'])) {
                    UpgradeLog::where('id', $logId)->update([
                        'backup_path' => $report['backup_path'],
                    ]);
                }
                throw $installError;
            }

            @unlink($download['file_path']);

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

            UpgradeLog::where('id', $logId)->update([
                'status'       => 3,
                'progress'     => 100,
                'message'      => 'Upgrade complete',
                'backup_path'  => $report['backup_path'],
                'sql_files'    => json_encode($report['sql_files'], JSON_UNESCAPED_UNICODE),
                'finished_at'  => date('Y-m-d H:i:s'),
            ]);
        } catch (\Throwable $e) {
            Log::error('upgrade execute failed: ' . $e->getMessage() . "\n" . $e->getTraceAsString());

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

            try {
                $log = UpgradeLog::find($logId);
                if ($log && $log->backup_path) {
                    app()->make(UpgradeBackupService::class)->restore($log->backup_path);
                    $refresh = app()->make(UpgradeRuntimeRefreshService::class)->refresh('upgrade-auto-rollback');
                    UpgradeLog::where('id', $logId)->update([
                        'message' => (int)($refresh['code'] ?? 1) === 0
                            ? 'Upgrade failed, auto-rollback done'
                            : 'Upgrade failed, auto-rollback done but sync failed',
                    ]);
                }
            } catch (\Throwable $rollbackError) {
                Log::error('upgrade rollback failed: ' . $rollbackError->getMessage());
            }
        }
    }
}
