<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Responses\ApiResponse;
use App\Services\AppBackupService;
use App\Services\AppInstallerService;
use App\Services\AppManagerService;
use App\Services\AppMarketService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class AppController extends Controller
{
    protected AppManagerService $managerService;

    protected AppInstallerService $installerService;

    protected AppMarketService $marketService;

    protected AppBackupService $backupService;

    public function __construct(
        AppManagerService $managerService,
        AppInstallerService $installerService,
        AppMarketService $marketService,
        AppBackupService $backupService
    ) {
        $this->managerService = $managerService;
        $this->installerService = $installerService;
        $this->marketService = $marketService;
        $this->backupService = $backupService;
    }

    /**
     * 清理输出缓冲区并返回 JSON 响应
     *
     * 应用安装/卸载过程中会直接 require 迁移文件等 PHP 文件，
     * 若这些文件开头带有 UTF-8 BOM 或其他意外输出，会导致 JSON 响应体前混入
     * 非 JSON 字符，使前端 jQuery 解析失败进入 error 回调。
     * 此方法在返回 JSON 前清理所有输出层，确保响应体是纯 JSON。
     */
    protected function cleanJsonResponse(array $data): \Illuminate\Http\JsonResponse
    {
        while (ob_get_level() > 0) {
            ob_end_clean();
        }

        return response()->json($data);
    }

    public function index()
    {
        $result = $this->managerService->getInstalledApps();

        return response()->json($result);
    }

    public function available()
    {
        $result = $this->managerService->getAllAvailableApps();

        return response()->json($result);
    }

    public function show(string $appId)
    {
        $result = $this->managerService->getApp($appId);

        return response()->json($result);
    }

    public function upload(Request $request)
    {
        $request->validate([
            'package' => 'required|file|max:51200',
        ]);

        $file = $request->file('package');
        if (!$file || strtolower($file->getClientOriginalExtension()) !== 'zip') {
            return response()->json(\App\Http\Responses\ApiResponse::error(40001, '仅支持 .zip 格式的应用包'));
        }

        $targetAppId = $request->input('app_id');

        $result = $this->installerService->uploadAndInstall($file, $targetAppId);

        return $this->cleanJsonResponse($result);
    }

    public function install(string $appId)
    {
        $result = $this->installerService->installFromMarket($appId);

        return $this->cleanJsonResponse($result);
    }

    public function localInstall(string $appId)
    {
        $result = $this->installerService->installFromLocal($appId);

        return $this->cleanJsonResponse($result);
    }

    public function uninstall(string $appId)
    {
        $result = $this->installerService->uninstall($appId);

        return $this->cleanJsonResponse($result);
    }

    public function delete(string $appId)
    {
        $result = $this->installerService->deleteAppFiles($appId);

        return $this->cleanJsonResponse($result);
    }

    public function icon(string $appId)
    {
        $appsPath = app_path('Apps');
        if (is_dir($appsPath)) {
            $dirs = scandir($appsPath);
            foreach ($dirs as $dir) {
                if ($dir === '.' || $dir === '..') {
                    continue;
                }

                $manifestPath = $appsPath . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . 'manifest.json';
                if (! file_exists($manifestPath)) {
                    continue;
                }

                $manifest = json_decode(file_get_contents($manifestPath), true);
                if (($manifest['id'] ?? '') === $appId) {
                    $appDir = $appsPath . DIRECTORY_SEPARATOR . $dir;

                    $svgPath = $appDir . DIRECTORY_SEPARATOR . 'icon.svg';
                    if (file_exists($svgPath)) {
                        return response()->file($svgPath, [
                            'Content-Type' => 'image/svg+xml',
                            'Cache-Control' => 'public, max-age=86400',
                        ]);
                    }

                    $pngPath = $appDir . DIRECTORY_SEPARATOR . 'icon.png';
                    if (file_exists($pngPath)) {
                        return response()->file($pngPath, [
                            'Content-Type' => 'image/png',
                            'Cache-Control' => 'public, max-age=86400',
                        ]);
                    }
                    break;
                }
            }
        }

        abort(404);
    }

    public function enable(string $appId)
    {
        $result = $this->managerService->enableApp($appId);

        return response()->json($result);
    }

    public function disable(string $appId)
    {
        $result = $this->managerService->disableApp($appId);

        return response()->json($result);
    }

    public function upgrade(string $appId, Request $request)
    {
        $targetVersion = (string) $request->query('version', '');
        $result = $this->installerService->upgrade($appId, $targetVersion);

        return $this->cleanJsonResponse($result);
    }

    public function export(string $appId)
    {
        $result = $this->installerService->export($appId);

        if (is_array($result)) {
            return response()->json($result);
        }

        $zipFilePath = $result;
        if (!file_exists($zipFilePath)) {
            return response()->json(\App\Http\Responses\ApiResponse::error(50001, '导出包生成失败'));
        }

        return response()->download($zipFilePath, basename($zipFilePath), [
            'Content-Type' => 'application/zip',
        ])->deleteFileAfterSend(true);
    }

    public function checkUpdates(Request $request)
    {
        $lazy = (bool) $request->query('lazy', false);
        $result = $this->managerService->checkUpdates($lazy);

        return response()->json($result);
    }

    public function getConfig(string $appId)
    {
        $result = $this->managerService->getAppConfig($appId);

        return response()->json($result);
    }

    public function saveConfig(Request $request, string $appId)
    {
        $result = $this->managerService->saveAppConfig($appId, $request->all());

        return response()->json($result);
    }

    public function marketApps(Request $request)
    {
        $result = $this->marketService->getApps($request->all());

        return response()->json($result);
    }

    public function marketAppDetail(string $appId)
    {
        $result = $this->marketService->getAppDetail($appId);

        return response()->json($result);
    }

    public function marketCategories()
    {
        $result = $this->marketService->getCategories();

        return response()->json($result);
    }

    /**
     * 创建购买订单（系统级 API，远程调用 v5.cmspro.cn）
     *
     * 客户站点后台应用市场购买应用时调用，通过 AppMarketService 远程请求
     * v5.cmspro.cn 的 /api/market/purchase/create 接口创建支付订单。
     */
    public function marketPurchaseCreate(Request $request)
    {
        // 记录完整的请求参数
        $requestData = $request->all();
        Log::info('购买订单创建请求开始', [
            'request_data' => $requestData,
            'remote_addr' => $request->getClientIp(),
            'user_agent' => $request->userAgent(),
            'headers' => $request->headers->all()
        ]);

        try {
            $request->validate([
                'app_id' => 'required|string|max:64',
                'channel' => 'nullable|string|in:wechat,alipay,qqpay,unionpay',
                'pay_method' => 'nullable|string|in:native,h5,jsapi',
                'subject' => 'nullable|string|max:200',
                'amount' => 'nullable|numeric|min:0.01',
            ], [
                'app_id.required' => 'app_id参数不能为空',
                'app_id.max' => 'app_id参数长度不能超过64个字符',
                'channel.in' => 'channel参数值不合法，只允许：wechat,alipay,qqpay,unionpay',
                'pay_method.in' => 'pay_method参数值不合法，只允许：native,h5,jsapi',
                'amount.min' => 'amount参数值必须大于等于0.01',
            ]);
            
            Log::info('购买订单请求参数验证通过', ['validated_data' => $requestData]);

        } catch (\ValidationException $e) {
            Log::warning('购买订单请求参数验证失败', [
                'errors' => $e->validator->errors()->toArray(),
                'request_data' => $requestData,
                'validation_rules' => [
                    'app_id' => 'required|string|max:64',
                    'channel' => 'nullable|string|in:wechat,alipay,qqpay,unionpay',
                    'pay_method' => 'nullable|string|in:native,h5,jsapi',
                    'subject' => 'nullable|string|max:200',
                    'amount' => 'nullable|numeric|min:0.01'
                ]
            ]);
            
            return response()->json([
                'code' => 40201,
                'message' => '请求参数不合法：' . $e->validator->errors()->first(),
                'data' => null
            ], 200);
        }

        $adminId = auth('admin')->id();
        $appId = $request->input('app_id');
        
        Log::info('验证用户信息', [
            'admin_id' => $adminId,
            'app_id' => $appId
        ]);

        // 生成商户订单号（符合微信支付规范：<=32 字符，字母数字下划线）
        $outTradeNo = 'MKT' . dechex($adminId) . '_' . time() . '_' . mt_rand(1000, 9999);
        Log::info('生成商户订单号', [
            'admin_id' => $adminId,
            'out_trade_no' => $outTradeNo
        ]);

        $data = [
            'out_trade_no' => $outTradeNo,
            'app_id' => $appId,
            'channel' => $request->input('channel', 'wechat'),
            'pay_method' => $request->input('pay_method', 'native'),
            'subject' => $request->input('subject', '购买应用：' . $appId),
        ];

        if ($request->filled('amount')) {
            $data['amount'] = $request->input('amount');
            Log::info('应用金额参数', ['amount' => $data['amount']]);
        }
        
        Log::info('最终请求数据', [
            'data' => $data,
            'adminId' => $adminId,
            'outTradeNo' => $outTradeNo
        ]);

        $result = $this->marketService->createPurchase($data);
        
        Log::info('购买订单创建最终结果', [
            'result' => $result,
            'out_trade_no' => $outTradeNo,
            'adminId' => $adminId,
            'app_id' => $appId
        ]);

        // 补充 out_trade_no 给前端（用于轮询查询）
        if (isset($result['data']) && is_array($result['data'])) {
            $result['data']['out_trade_no'] = $outTradeNo;
            Log::info('补充订单号', ['out_trade_no' => $outTradeNo]);
        }

        return response()->json($result);
    }

    /**
     * 查询购买订单状态
     */
    public function marketPurchaseStatus(Request $request)
    {
        $request->validate([
            'out_trade_no' => 'required|string|max:64',
        ]);

        $result = $this->marketService->queryPurchase($request->input('out_trade_no'));

        return response()->json($result);
    }

    /**
     * 已购买应用列表
     */
    public function marketPurchaseList()
    {
        $result = $this->marketService->getPurchasedApps();

        return response()->json($result);
    }

    /**
     * 站点注册状态
     */
    public function marketSiteStatus()
    {
        $result = $this->marketService->getSiteStatus();

        return response()->json($result);
    }

    /**
     * 注册站点到 v5.cmspro.cn
     */
    public function marketSiteRegister()
    {
        $force = (bool) request()->input('force', false);
        $result = $this->marketService->registerSite($force);

        return response()->json($result);
    }

    /**
     * 站点身份验证（供 v5.cmspro.cn 反向调用）
     *
     * v5.cmspro.cn 注册/校验站点时，会通过 HTTP 请求客户站点的此接口，
     * 客户站点返回本地 CMSPRO_SITE_KEY，服务端据此验证域名所有权。
     */
    public function marketSiteVerifyIdentity()
    {
        $siteKey = (string) config('apps.site_key', '');

        return response()->json([
            'site_key' => $siteKey,
            'domain' => request()->getHost(),
        ]);
    }

    /**
     * 同步站点身份到服务端（域名变更后使用）
     */
    public function marketSiteSync()
    {
        $result = $this->marketService->syncSiteIdentity();

        return response()->json($result);
    }

    public function marketInstall(string $appId)
    {
        // 检查付费应用是否已购买（远程校验）
        $appResult = $this->marketService->getAppDetail($appId);
        if (isset($appResult['data']) && (float) ($appResult['data']['price'] ?? 0) > 0) {
            $purchasedResult = $this->marketService->getPurchasedApps();
            $purchasedAppIds = collect($purchasedResult['data'] ?? [])->pluck('app_id')->map(fn ($v) => strtolower($v))->toArray();
            if (!in_array(strtolower($appId), $purchasedAppIds, true)) {
                return response()->json(ApiResponse::error(50010, '该应用为付费应用，请先购买'));
            }
        }

        try {
            $zipPath = $this->marketService->downloadPackage($appId, 'latest');
            $result = $this->installerService->installFromZip($zipPath);
        } catch (\Throwable $e) {
            $result = ApiResponse::error(50013, $e->getMessage());
        }

        return $this->cleanJsonResponse($result);
    }

    public function logs(Request $request)
    {
        $result = $this->managerService->getOperationLogs($request->all());

        return response()->json($result);
    }

    public function backupList(string $appId)
    {
        $result = $this->backupService->getBackupList($appId);

        return response()->json($result);
    }

    public function backupCreate(string $appId, Request $request)
    {
        $volumeSize = (int) $request->input('volume_size', 2);
        $volumeSize = max(1, min(100, $volumeSize));
        $result = $this->backupService->createBackup($appId, $volumeSize);

        return response()->json($result);
    }

    /**
     * 备份 - 第一步：准备
     */
    public function backupPrepare(string $appId, Request $request): \Illuminate\Http\JsonResponse
    {
        $volumeSize = (int) $request->input('volume_size', 2);
        $volumeSize = max(1, min(100, $volumeSize));

        $result = $this->backupService->prepareBackup($appId, $volumeSize);

        return response()->json($result);
    }

    /**
     * 备份 - 第二步：备份单表（支持分页）
     */
    public function backupTable(string $appId, Request $request): \Illuminate\Http\JsonResponse
    {
        $sessionId = $request->input('session_id');
        $table = $request->input('table');
        $volumeSize = (int) $request->input('volume_size', 2);
        $volumeSize = max(1, min(100, $volumeSize));
        $lastIdInput = $request->input('last_id');

        if (empty($sessionId) || empty($table)) {
            return response()->json(\App\Http\Responses\ApiResponse::error(40001, 'session_id 和 table 不能为空'));
        }

        // last_id 为空字符串或 null 时表示第一页
        $lastId = null;
        if ($lastIdInput !== null && $lastIdInput !== '' && $lastIdInput !== 'null') {
            $lastId = (int) $lastIdInput;
        }

        $result = $this->backupService->backupTable($appId, $sessionId, $table, $volumeSize, $lastId);

        return response()->json($result);
    }

    /**
     * 备份 - 第三步：完成
     */
    public function backupFinish(string $appId, Request $request): \Illuminate\Http\JsonResponse
    {
        $sessionId = $request->input('session_id');
        if (empty($sessionId)) {
            return response()->json(\App\Http\Responses\ApiResponse::error(40001, 'session_id 不能为空'));
        }

        $result = $this->backupService->finishBackup($appId, $sessionId);

        return response()->json($result);
    }

    public function backupRestore(int $backupId)
    {
        $result = $this->backupService->restoreBackup($backupId);

        return response()->json($result);
    }

    public function backupDelete(int $backupId)
    {
        $result = $this->backupService->deleteBackup($backupId);

        return response()->json($result);
    }

    public function backupDownload(int $backupId)
    {
        $filePath = $this->backupService->getBackupFilePath($backupId);
        if (! $filePath) {
            return response()->json(\App\Http\Responses\ApiResponse::error(50023, '备份文件不存在或已失效'));
        }

        $fileName = basename($filePath);

        return response()->download($filePath, $fileName, [
            'Content-Type' => 'application/zip',
        ]);
    }

    public function backupImport(Request $request, string $appId)
    {
        $request->validate([
            'package' => 'required|file|max:512000',
        ]);

        $file = $request->file('package');
        if (strtolower($file->getClientOriginalExtension()) !== 'zip') {
            return response()->json(\App\Http\Responses\ApiResponse::error(40001, '仅支持 .zip 格式的备份包'));
        }

        $result = $this->backupService->importBackup($appId, $file);

        return response()->json($result);
    }

    /**
     * 列出服务器本地备份目录中的文件
     */
    public function backupLocalFiles(string $appId)
    {
        $result = $this->backupService->listLocalBackupFiles($appId);

        return response()->json($result);
    }

    /**
     * 上传统份文件到服务器（仅保存，不恢复）
     */
    public function backupUploadFile(Request $request, string $appId)
    {
        $request->validate([
            'package' => 'required|file|max:512000',
        ]);

        $file = $request->file('package');
        if (strtolower($file->getClientOriginalExtension()) !== 'zip') {
            return response()->json(\App\Http\Responses\ApiResponse::error(40001, '仅支持 .zip 格式的备份包'));
        }

        $result = $this->backupService->uploadBackupFile($appId, $file);

        return response()->json($result);
    }

    /**
     * 从服务器本地备份文件恢复应用
     */
    public function backupImportLocal(Request $request, string $appId)
    {
        $request->validate([
            'file_name' => 'required|string|max:255',
        ]);

        $fileName = $request->input('file_name');
        $result = $this->backupService->importLocalBackup($appId, $fileName);

        return response()->json($result);
    }

    /**
     * 分步恢复 - 第一步：准备
     */
    public function restorePrepare(Request $request, string $appId)
    {
        $backupId = $request->input('backup_id') ? (int) $request->input('backup_id') : null;
        $localFileName = $request->input('local_file_name');

        $result = $this->backupService->prepareRestore($appId, $backupId, $localFileName);

        return response()->json($result);
    }

    /**
     * 分步恢复 - 第二步：恢复单表
     */
    public function restoreTableStep(Request $request, string $appId)
    {
        $sessionId = $request->input('session_id');
        $table = $request->input('table');
        $offset = (int) ($request->input('offset', 0));

        if (empty($sessionId) || empty($table)) {
            return response()->json(\App\Http\Responses\ApiResponse::error(40001, 'session_id 和 table 不能为空'));
        }

        $result = $this->backupService->restoreTableStep($appId, $sessionId, $table, $offset);

        return response()->json($result);
    }

    /**
     * 分步恢复 - 第三步：完成
     */
    public function restoreFinish(Request $request, string $appId)
    {
        $sessionId = $request->input('session_id');
        if (empty($sessionId)) {
            return response()->json(\App\Http\Responses\ApiResponse::error(40001, 'session_id 不能为空'));
        }

        $result = $this->backupService->finishRestore($appId, $sessionId);

        return response()->json($result);
    }
}
