<?php
require_once __DIR__ . '/../base_api.php';
require_once __DIR__ . '/json_helper.php';

class DeployAPI extends BaseAPI {
    private $site_root;

    public function __construct() {
        parent::__construct();
        $this->initSiteRoot();
    }

    private function initSiteRoot() {
        $username = isset($this->user->user) ? $this->user->user : (isset($this->user['user']) ? $this->user['user'] : null);
        if (!$username) {
            $this->error('用户名不存在');
        }

        $conf = isset($GLOBALS['conf']) ? $GLOBALS['conf'] : null;

        if (!$conf) {
            global $DB;
            $conf = $DB->get_row("SELECT * FROM `config` WHERE `id`=1");
        }
        $linux_path = isset($conf['hxi']) && !empty($conf['hxi']) ? $conf['hxi'] : '/www/wwwroot';
        $windows_path = isset($conf['hxo']) && !empty($conf['hxo']) ? $conf['hxo'] : 'D:/wwwroot';
        $os_type = isset($this->bt_info['czzt']) ? $this->bt_info['czzt'] : 'Linux';
        $this->site_root = ($os_type == 'Linux' ? $linux_path : $windows_path) . '/' . $username;
    }

    private function ensureDeployLogTable() {
        global $DB;
        $table_exists = $DB->get_row("SHOW TABLES LIKE 'bsjl'");
        if ($table_exists) {
            return;
        }
        $sql = "CREATE TABLE IF NOT EXISTS `bsjl` (
            `id` int(11) NOT NULL AUTO_INCREMENT,
            `uid` int(11) NOT NULL,
            `bs_id` int(11) NOT NULL,
            `time` datetime NOT NULL,
            `zt` varchar(50) NOT NULL,
            PRIMARY KEY (`id`)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8";
        $DB->query($sql);
    }

    private function detectArchiveType($filename) {
        $lower = strtolower($filename);
        if (substr($lower, -7) === '.tar.gz') {
            return 'tar.gz';
        }
        if (substr($lower, -4) === '.tgz') {
            return 'tar.gz';
        }
        $ext = pathinfo($lower, PATHINFO_EXTENSION);
        return $ext;
    }

    /**
     * 清空网站文件
     * @param string $site_root 网站根目录
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function clearSiteFiles($site_root) {
        // 获取目录下的所有文件和文件夹，然后逐个删除（保留目录本身）
        return $this->clearSiteFilesOneByOne($site_root);
    }

    /**
     * 逐个删除网站文件（备用方案）
     * @param string $site_root 网站根目录
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function clearSiteFilesOneByOne($site_root) {
        // 获取目录下的所有文件和文件夹
        $url = $this->bt_api->BT_PANEL . '/files?action=GetDir';
        $p_data = $this->bt_api->GetKeyData();
        $p_data['path'] = $site_root;
        $p_data['page'] = 1;
        $p_data['showRow'] = 1000;
        $p_data['search'] = '';

        $result = $this->bt_api->HttpPostCookie($url, $p_data);

        $response = json_decode($result, true);

        // 如果返回的是空数组，说明目录为空，这是正常的
        if (is_array($response) && empty($response)) {
            return array('success' => true, 'msg' => '目录为空');
        }

        // 检查返回的数据结构
        if (is_array($response) && !isset($response['status'])) {
            // 可能是直接返回的文件列表
            if (count($response) === 0) {
                return array('success' => true, 'msg' => '目录为空');
            }
            // 直接使用返回的数组作为文件列表
            $file_list = $response;
        } elseif (!$response || !isset($response['status']) || ($response['status'] !== true && $response['status'] !== 'true' && $response['status'] !== 1)) {
            // 目录可能是空的，这不算是错误
            if (isset($response['msg']) && (strpos($response['msg'], 'empty') !== false || strpos($response['msg'], '不存在') !== false || strpos($response['msg'], 'not exist') !== false)) {
                return array('success' => true, 'msg' => '目录为空');
            }
            return array('success' => false, 'msg' => '获取目录内容失败: ' . (isset($response['msg']) ? $response['msg'] : '未知错误'));
        } else {
            // 正常返回，使用DATA字段
            $file_list = isset($response['DATA']) ? $response['DATA'] : array();
        }

        // 删除所有文件和文件夹
        $delete_errors = [];
        if (is_array($file_list) && count($file_list) > 0) {

            // 第一步：先删除所有文件（非目录）
            foreach ($file_list as $item) {
                if (!isset($item['name'])) {
                    continue;
                }
                // 跳过目录，先删除文件
                if (isset($item['type']) && $item['type'] == 'dir') {
                    continue;
                }

                $file_path = $site_root . '/' . $item['name'];

                // 删除文件
                $del_url = $this->bt_api->BT_PANEL . '/files?action=DeleteFile';
                $del_data = $this->bt_api->GetKeyData();
                $del_data['path'] = $file_path;

                $result = $this->bt_api->HttpPostCookie($del_url, $del_data);
                $result_data = json_decode($result, true);

                // 检查删除结果
                if (!$result_data || !isset($result_data['status']) || ($result_data['status'] !== true && $result_data['status'] !== 'true' && $result_data['status'] !== 1)) {
                    $error_msg = isset($result_data['msg']) ? $result_data['msg'] : '删除失败';
                    $delete_errors[] = $item['name'] . ': ' . $error_msg;
                } else {
                }
            }

            // 第二步：再删除所有目录（使用递归删除）
            foreach ($file_list as $item) {
                if (!isset($item['name'])) {
                    continue;
                }
                // 只处理目录
                if (!isset($item['type']) || $item['type'] != 'dir') {
                    continue;
                }

                $file_path = $site_root . '/' . $item['name'];

                // 删除文件夹 - 使用递归删除
                $del_url = $this->bt_api->BT_PANEL . '/files?action=DeleteDir';
                $del_data = $this->bt_api->GetKeyData();
                $del_data['path'] = $file_path;
                // 添加递归删除参数，确保能删除非空文件夹
                $del_data['recursively'] = '1';

                $result = $this->bt_api->HttpPostCookie($del_url, $del_data);
                $result_data = json_decode($result, true);

                // 检查删除结果
                if (!$result_data || !isset($result_data['status']) || ($result_data['status'] !== true && $result_data['status'] !== 'true' && $result_data['status'] !== 1)) {
                    $error_msg = isset($result_data['msg']) ? $result_data['msg'] : '删除失败';
                    $delete_errors[] = $item['name'] . ': ' . $error_msg;
                } else {
                }
            }
        } else {
        }

        if (!empty($delete_errors)) {
            // 不中断部署，只记录错误
        }

        return array('success' => true, 'msg' => '清空完成' . (count($delete_errors) > 0 ? ' (部分文件删除失败)' : ''));
    }

    /**
     * 清空用户数据库
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function clearDatabase() {
        global $DB;
        $uid = isset($this->user['id']) ? intval($this->user['id']) : 0;

        // 获取用户的数据库信息
        $uid_escaped = $DB->escape($uid);
        $user_db = $DB->get_row("SELECT * FROM `sjk` WHERE `uid`='{$uid_escaped}' LIMIT 1");
        if (!$user_db) {
            return array('success' => true, 'msg' => '用户没有数据库，跳过');
        }

        // 安全处理数据库名
        $db_name = preg_replace('/[^a-zA-Z0-9_]/', '', $user_db['name']);
        if (empty($db_name)) {
            return array('success' => false, 'msg' => '数据库名称无效');
        }

        // 获取所有表
        $tables_result = $DB->query("SHOW TABLES LIKE '" . $db_name . "_%'");
        if (!$tables_result) {
            return array('success' => false, 'msg' => '获取表列表失败');
        }

        $dropped_count = 0;
        while ($row = $DB->fetch($tables_result)) {
            $table_name = current($row);
            // 安全处理表名
            $safe_table_name = preg_replace('/[^a-zA-Z0-9_]/', '', $table_name);
            if (!empty($safe_table_name)) {
                $DB->query("DROP TABLE IF EXISTS `" . $safe_table_name . "`");
                $dropped_count++;
            }
        }

        return array('success' => true, 'msg' => "已清空 {$dropped_count} 个表");
    }

    private function unzipArchive($targetDir, $fileName, $type) {
        $url = $this->bt_api->BT_PANEL . '/files?action=UnZip';
        $p_data = $this->bt_api->GetKeyData();
        $p_data['sfile'] = rtrim($targetDir, '/') . '/' . $fileName;
        $p_data['dfile'] = $targetDir;
        $p_data['coding'] = 'utf-8';
        $p_data['password'] = '';
        $p_data['type'] = $type;
        $result = $this->bt_api->HttpPostCookie($url, $p_data);
        return json_decode($result, true);
    }

    /**
     * 删除压缩包文件
     * @param string $filePath 文件完整路径
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function deleteArchiveFile($filePath) {
        $url = $this->bt_api->BT_PANEL . '/files?action=DeleteFile';
        $p_data = $this->bt_api->GetKeyData();
        $p_data['path'] = $filePath;
        
        $result = $this->bt_api->HttpPostCookie($url, $p_data);
        $response = json_decode($result, true);
        
        if ($response && isset($response['status']) && ($response['status'] === true || $response['status'] === 'true' || $response['status'] === 1)) {
            return array('success' => true, 'msg' => '文件删除成功');
        } else {
            $error_msg = isset($response['msg']) ? $response['msg'] : '删除失败';
            return array('success' => false, 'msg' => $error_msg);
        }
    }

    public function deployProgram($programId) {
        global $DB;
        $id = intval($programId);
        if ($id <= 0) {
            $this->error('参数错误');
        }

        $id_escaped = $DB->escape($id);
        $program = $DB->get_row("SELECT * FROM `bs` WHERE `id`='{$id_escaped}'");
        if (!$program) {
            $this->error('程序不存在');
        }

        // 检查是否已购买（免费程序或已购买）
        $uid = isset($this->user['id']) ? intval($this->user['id']) : 0;
        $price = floatval($program['jg']);
        if ($price > 0) {
            $uid_escaped = $DB->escape($uid);
        $id_escaped = $DB->escape($id);
        $purchased = $DB->get_row("SELECT * FROM `bs_purchase` WHERE `uid`='{$uid_escaped}' AND `bs_id`='{$id_escaped}' AND `status`=1");
            if (!$purchased) {
                $this->error('您尚未购买此程序，请先购买');
            }
        }

        // 获取用户提交的表单字段数据
        $form_fields_data = array();
        if (!empty($program['form_fields'])) {
            // 使用 JsonHelper 安全解析JSON
            $form_fields_config = JsonHelper::parse($program['form_fields']);
            if (is_array($form_fields_config)) {
                foreach ($form_fields_config as $field) {
                    $field_name = isset($field['name']) ? $field['name'] : '';
                    if (!empty($field_name)) {
                        $form_fields_data[$field_name] = isset($_POST['form_fields'][$field_name]) ? $_POST['form_fields'][$field_name] : '';
                    }
                }
            }
        }

        $package = '';
        if (!empty($program['wj'])) {
            $package = $program['wj'];
        } elseif (!empty($program['file'])) {
            $package = $program['file'];
        }

        if (empty($package)) {
            $this->error('程序包不存在');
        }

        $package = basename($package);

        // 尝试在 uploads/programs 目录查找
        $upload_dir = @realpath(__DIR__ . '/../../../uploads/programs');
        if (!$upload_dir) {
            // 如果 programs 目录不存在，尝试 uploads 目录
            $upload_dir = @realpath(__DIR__ . '/../../../uploads');
        }
        if (!$upload_dir) {
            $this->error('程序包目录不存在');
        }

        $package_path = rtrim($upload_dir, '/') . '/' . $package;
        if (!file_exists($package_path) || !is_readable($package_path)) {
            // 尝试在 uploads 目录查找（兼容旧数据）
            $upload_dir_fallback = @realpath(__DIR__ . '/../../../uploads');
            if ($upload_dir_fallback) {
                $package_path_fallback = rtrim($upload_dir_fallback, '/') . '/' . $package;
                if (file_exists($package_path_fallback) && is_readable($package_path_fallback)) {
                    $package_path = $package_path_fallback;
                } else {
                    $this->error('程序包文件不存在: ' . $package);
                }
            } else {
                $this->error('程序包文件不存在: ' . $package);
            }
        }

        $archive_type = $this->detectArchiveType($package);
        $allowed_types = array('zip', 'tar', 'gz', 'tar.gz', 'tgz');
        if (!in_array($archive_type, $allowed_types)) {
            $this->error('程序包格式不支持');
        }

        // 步骤1: 清空网站文件
        $clear_result = $this->clearSiteFiles($this->site_root);
        if (!$clear_result['success']) {
            $this->error('清空网站文件失败: ' . $clear_result['msg']);
        }

        // 步骤2: 清空数据库
        $clear_db_result = $this->clearDatabase();
        if (!$clear_db_result['success']) {
            // 数据库清空失败不阻止部署，只记录日志
        } else {
        }

        // 调试信息
        $file_exists = file_exists($package_path);
        $file_size = $file_exists ? filesize($package_path) : 0;
        
        // 增加内存限制
        ini_set('memory_limit', '512M');
        
        try {
            $upload_result = $this->bt_api->UploadFile($this->site_root, $package_path);
        } catch (Exception $e) {
            $this->error('上传异常: ' . $e->getMessage());
        }
        
        
        // 检查上传结果
        if ($upload_result === null) {
            $this->error('上传程序包失败: 宝塔API返回无效响应，请检查宝塔面板连接');
        }
        
        if ($upload_result === false) {
            $this->error('上传程序包失败: 请求宝塔API失败，请检查网络连接');
        }
        
        if (!is_array($upload_result)) {
            $this->error('上传程序包失败: 宝塔API返回格式错误');
        }
        
        if (!isset($upload_result['status']) || ($upload_result['status'] !== true && $upload_result['status'] !== 'true' && $upload_result['status'] !== 1)) {
            $error_msg = isset($upload_result['msg']) ? $upload_result['msg'] : '上传程序包失败';
            $error_detail = json_encode($upload_result);
            $this->error('上传程序包失败: ' . $error_msg . ' (详细: ' . $error_detail . ')');
        }

        $unzip_result = $this->unzipArchive($this->site_root, $package, $archive_type);
        if (!$unzip_result || !isset($unzip_result['status']) || ($unzip_result['status'] !== true && $unzip_result['status'] !== 'true' && $unzip_result['status'] !== 1)) {
            $error_msg = isset($unzip_result['msg']) ? $unzip_result['msg'] : '解压失败';
            $this->error($error_msg);
        }

        // Step 3.5: 删除压缩包文件（部署完成后清理）
        $archive_path = rtrim($this->site_root, '/') . '/' . $package;
        $delete_result = $this->deleteArchiveFile($archive_path);
        if ($delete_result['success']) {
        } else {
            // 删除失败不中断部署，只是记录日志
        }

        // Step 4: 设置PHP版本（如果程序有指定）
        if (!empty($program['php_version'])) {
            $php_result = $this->setSitePHPVersion($program['php_version']);
            if (!$php_result['success']) {
                // 不中断部署，只是记录错误
            }
        }

        // Step 5: 执行安装操作
        if (!empty($program['install_ops'])) {
            $ops_result = $this->executeInstallOps($program['install_ops'], $form_fields_data);
            if (!$ops_result['success']) {
                // 不中断部署，只是记录错误
            }
        }

        $this->ensureDeployLogTable();
        $uid = isset($this->user['id']) ? intval($this->user['id']) : 0;
        $uid_escaped = $DB->escape($uid);
        $id_escaped = $DB->escape($id);
        $DB->query("INSERT INTO `bsjl` (`uid`, `bs_id`, `time`, `zt`) VALUES ('{$uid_escaped}', '{$id_escaped}', NOW(), '已部署')");

        $this->log('一键部署', '部署程序: ' . $program['name']);
        $this->success(array('msg' => '部署成功'));
    }

    /**
     * 设置网站PHP版本
     * @param string $php_version PHP版本号，如 "7.4"
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function setSitePHPVersion($php_version) {
        // 获取网站ID
        $webid = isset($this->user['webid']) ? $this->user['webid'] : (isset($this->user->webid) ? $this->user->webid : null);
        if (empty($webid)) {
            return array('success' => false, 'msg' => '网站ID不存在');
        }

        // 获取完整的网站名称（通过宝塔API）
        $site_name = $this->bt_api->GetSiteName($webid);
        if (empty($site_name)) {
            return array('success' => false, 'msg' => '获取网站名称失败');
        }

        // 转换PHP版本格式（如 "7.4" -> "74"）
        $php_version_num = str_replace('.', '', $php_version);
        
        
        // 调用宝塔API设置PHP版本 - 使用完整的网站名称
        $result = $this->bt_api->SetPHPVersion($site_name, $php_version_num);
        
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            // 更新数据库中的PHP版本记录
            global $DB;
            $uid = isset($this->user['id']) ? intval($this->user['id']) : 0;
            if ($uid > 0) {
                $php_version_num_escaped = $DB->escape($php_version_num);
        $uid_escaped = $DB->escape($uid);
        $DB->query("UPDATE `zj` SET `php_version`='{$php_version_num_escaped}' WHERE `id`='{$uid_escaped}'");
            }
            return array('success' => true, 'msg' => 'PHP版本设置成功');
        } else {
            $error_msg = isset($result['msg']) ? $result['msg'] : '设置PHP版本失败';
            return array('success' => false, 'msg' => $error_msg);
        }
    }

    /**
     * 执行安装操作
     * @param string $install_ops_json 安装操作JSON字符串
     * @param array $form_fields_data 表单字段数据
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function executeInstallOps($install_ops_json, $form_fields_data = array()) {
        // 使用JsonHelper解析JSON
        $ops = JsonHelper::parse($install_ops_json);
        if ($ops === false) {
            $error_msg = JsonHelper::getLastError();
            return array('success' => false, 'msg' => '安装操作解析失败: ' . $error_msg);
        }
        
        if (empty($ops) || !is_array($ops)) {
            return array('success' => true, 'msg' => '没有安装操作需要执行');
        }

        foreach ($ops as $index => $op) {
            $type = isset($op['type']) ? $op['type'] : '';

            switch ($type) {
                case 'rewrite':
                case 'setrewrite':
                    // 设置伪静态
                    $content = isset($op['content']) ? $op['content'] : '';
                    // 替换变量
                    $content = $this->replaceVariables($content, $form_fields_data);
                    if (!empty($content)) {
                        $result = $this->setRewriteRule($content);
                        if (!$result['success']) {
                            return $result;
                        }
                    }
                    break;

                case 'setrundir':
                    // 设置运行目录
                    $path = isset($op['path']) ? $op['path'] : '';
                    // 替换变量
                    $path = $this->replaceVariables($path, $form_fields_data);
                    if (!empty($path)) {
                        $result = $this->setRunDir($path);
                        if (!$result['success']) {
                            return $result;
                        }
                    }
                    break;

                case 'sql':
                case 'importsql':
                    // 导入数据库（这个应该在清空数据库后执行）
                    $sql_path = isset($op['sql_file']) ? $op['sql_file'] : (isset($op['path']) ? $op['path'] : '');
                    // 替换变量
                    $sql_path = $this->replaceVariables($sql_path, $form_fields_data);
                    if (!empty($sql_path)) {
                        $result = $this->importSQL($sql_path, $form_fields_data);
                        if (!$result['success']) {
                            return $result;
                        }
                    }
                    break;

                case 'rename':
                    // 重命名文件/文件夹
                    $old_path = isset($op['old_path']) ? $op['old_path'] : '';
                    $new_path = isset($op['new_path']) ? $op['new_path'] : '';
                    // 替换变量
                    $old_path = $this->replaceVariables($old_path, $form_fields_data);
                    $new_path = $this->replaceVariables($new_path, $form_fields_data);
                    if (!empty($old_path) && !empty($new_path)) {
                        $result = $this->renameFile($old_path, $new_path);
                        if (!$result['success']) {
                            return $result;
                        }
                    }
                    break;

                case 'extract':
                    // 解压文件
                    $source = isset($op['source']) ? $op['source'] : '';
                    $target = isset($op['target']) ? $op['target'] : '';
                    // 替换变量
                    $source = $this->replaceVariables($source, $form_fields_data);
                    $target = $this->replaceVariables($target, $form_fields_data);
                    if (!empty($source) && !empty($target)) {
                        $result = $this->extractArchive($source, $target);
                        if (!$result['success']) {
                            return $result;
                        }
                    }
                    break;

                case 'config':
                    // 修改配置文件
                    $config_file = isset($op['config_file']) ? $op['config_file'] : '';
                    $content = isset($op['content']) ? $op['content'] : '';
                    // 替换变量
                    $config_file = $this->replaceVariables($config_file, $form_fields_data);
                    $content = $this->replaceVariables($content, $form_fields_data);
                    if (!empty($config_file)) {
                        $result = $this->writeConfigFile($config_file, $content);
                        if (!$result['success']) {
                            return $result;
                        }
                    }
                    break;

                case 'command':
                    // 执行命令
                    $command = isset($op['command']) ? $op['command'] : '';
                    // 替换变量
                    $command = $this->replaceVariables($command, $form_fields_data);
                    if (!empty($command)) {
                        $result = $this->executeCommand($command);
                        if (!$result['success']) {
                            return $result;
                        }
                    }
                    break;

                case 'httpget':
                    // HTTP GET请求
                    $url = isset($op['url']) ? $op['url'] : '';
                    if (!empty($url)) {
                        $result = $this->httpGet($url);
                        if (!$result['success']) {
                            return $result;
                        }
                    }
                    break;

                default:
                    break;
            }
        }

        return array('success' => true, 'msg' => '所有安装操作执行完成');
    }

    /**
     * 替换变量
     * @param string $content 内容
     * @param array $form_fields_data 表单字段数据
     * @return string 替换后的内容
     */
    private function replaceVariables($content, $form_fields_data) {
        if (empty($form_fields_data)) {
            return $content;
        }
        
        foreach ($form_fields_data as $key => $value) {
            $content = str_replace('{$' . $key . '}', $value, $content);
            $content = str_replace('${' . $key . '}', $value, $content);
            $content = str_replace('{' . $key . '}', $value, $content);
        }
        
        return $content;
    }

    /**
     * 设置伪静态规则
     * @param string $content 伪静态规则内容
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function setRewriteRule($content) {
        // 获取网站ID
        $webid = isset($this->user['webid']) ? $this->user['webid'] : (isset($this->user->webid) ? $this->user->webid : null);
        if (empty($webid)) {
            return array('success' => false, 'msg' => '网站ID不存在');
        }

        
        // 首先清空伪静态规则
        $clear_result = $this->bt_api->SetRewrite($webid, 'nginx', '');
        
        // 等待一下确保清空操作完成
        sleep(1);
        
        // 调用宝塔API设置伪静态 - 使用SetRewrite方法（需要网站ID）
        $result = $this->bt_api->SetRewrite($webid, 'nginx', $content);
        
        
        // SetRewrite返回的是array('status' => ..., 'msg' => ...)
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            return array('success' => true, 'msg' => '伪静态设置成功');
        } elseif (is_array($result) && isset($result[0]['status']) && ($result[0]['status'] === true || $result[0]['status'] === 'true' || $result[0]['status'] === 1)) {
            return array('success' => true, 'msg' => '伪静态设置成功');
        } else {
            $error_msg = isset($result['msg']) ? $result['msg'] : '设置伪静态失败';
            return array('success' => false, 'msg' => $error_msg);
        }
    }

    /**
     * 设置运行目录
     * @param string $path 运行目录路径
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function setRunDir($path) {
        // 获取网站ID
        $webid = isset($this->user['webid']) ? $this->user['webid'] : (isset($this->user->webid) ? $this->user->webid : null);
        if (empty($webid)) {
            return array('success' => false, 'msg' => '网站ID不存在');
        }

        
        // 调用宝塔API设置运行目录
        $result = $this->bt_api->SetSiteRunPath($webid, $path);
        
        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            return array('success' => true, 'msg' => '运行目录设置成功');
        } else {
            $error_msg = isset($result['msg']) ? $result['msg'] : '设置运行目录失败';
            return array('success' => false, 'msg' => $error_msg);
        }
    }

    /**
     * 导入SQL文件
     * @param string $sql_path SQL文件路径（相对网站根目录）
     * @param array $form_fields_data 表单字段数据
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function importSQL($sql_path, $form_fields_data = array()) {
        global $DB;

        $full_path = $this->site_root . '/' . ltrim($sql_path, '/');

        if (!file_exists($full_path)) {
            return array('success' => false, 'msg' => 'SQL文件不存在: ' . $sql_path);
        }

        // 获取用户数据库信息
        $uid = isset($this->user['id']) ? intval($this->user['id']) : 0;
        $uid_escaped = $DB->escape($uid);
        $user_db = $DB->get_row("SELECT * FROM `sjk` WHERE `uid`='{$uid_escaped}' LIMIT 1");

        if (!$user_db) {
            return array('success' => false, 'msg' => '用户没有数据库');
        }

        // 安全处理数据库名
        $db_name = preg_replace('/[^a-zA-Z0-9_]/', '', $user_db['name']);
        if (empty($db_name)) {
            return array('success' => false, 'msg' => '数据库名称无效');
        }

        // 读取SQL文件内容
        $sql_content = file_get_contents($full_path);
        if ($sql_content === false) {
            return array('success' => false, 'msg' => '读取SQL文件失败');
        }

        // 替换数据库前缀（如果有）
        $sql_content = str_replace('{db_prefix}', $db_name . '_', $sql_content);

        // 替换表单字段变量
        $sql_content = $this->replaceVariables($sql_content, $form_fields_data);

        // 解析SQL语句（处理字符串中的分号）
        $queries = $this->parseSqlStatements($sql_content);
        $success_count = 0;
        $error_count = 0;

        foreach ($queries as $query) {
            $query = trim($query);
            if (empty($query)) continue;

            // 跳过注释
            if (preg_match('/^(--|#|\/\*)/', $query)) continue;

            if ($DB->query($query)) {
                $success_count++;
            } else {
                $error_count++;
            }
        }


        if ($error_count > 0) {
            return array('success' => false, 'msg' => "SQL导入完成，成功: {$success_count}, 失败: {$error_count}");
        }

        return array('success' => true, 'msg' => 'SQL导入成功');
    }

    /**
     * 解析SQL语句，正确处理字符串中的分号
     * @param string $sql SQL文件内容
     * @return array SQL语句数组
     */
    private function parseSqlStatements($sql) {
        $statements = [];
        $currentStatement = '';
        $length = strlen($sql);
        $inString = false;
        $stringChar = '';
        $escapeNext = false;

        for ($i = 0; $i < $length; $i++) {
            $char = $sql[$i];

            // 处理转义字符
            if ($escapeNext) {
                $currentStatement .= $char;
                $escapeNext = false;
                continue;
            }

            if ($char === '\\') {
                $currentStatement .= $char;
                $escapeNext = true;
                continue;
            }

            // 处理字符串
            if (!$inString && ($char === "'" || $char === '"')) {
                $inString = true;
                $stringChar = $char;
                $currentStatement .= $char;
                continue;
            }

            if ($inString && $char === $stringChar) {
                $inString = false;
                $stringChar = '';
                $currentStatement .= $char;
                continue;
            }

            // 如果在字符串中，直接添加字符
            if ($inString) {
                $currentStatement .= $char;
                continue;
            }

            // 处理语句结束符
            if ($char === ';') {
                $statements[] = $currentStatement;
                $currentStatement = '';
                continue;
            }

            $currentStatement .= $char;
        }

        // 添加最后一个语句（如果没有以分号结束）
        if (trim($currentStatement) !== '') {
            $statements[] = $currentStatement;
        }

        return $statements;
    }

    /**
     * 重命名文件/文件夹
     * @param string $old_path 原路径
     * @param string $new_path 新路径
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function renameFile($old_path, $new_path) {
        $old_full = $this->site_root . '/' . ltrim($old_path, '/');
        $new_full = $this->site_root . '/' . ltrim($new_path, '/');
        
        
        if (!file_exists($old_full)) {
            return array('success' => false, 'msg' => '原文件不存在: ' . $old_path);
        }

        if (rename($old_full, $new_full)) {
            return array('success' => true, 'msg' => '重命名成功');
        } else {
            return array('success' => false, 'msg' => '重命名失败');
        }
    }

    /**
     * HTTP GET请求
     * @param string $url 请求URL
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function httpGet($url) {
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        
        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($http_code == 200) {
            return array('success' => true, 'msg' => 'HTTP请求成功');
        } else {
            return array('success' => false, 'msg' => 'HTTP请求失败，状态码: ' . $http_code);
        }
    }

    /**
     * 解压压缩包
     * @param string $source 压缩包路径
     * @param string $target 解压目标目录
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function extractArchive($source, $target) {

        // 检测压缩包类型
        $archive_type = $this->detectArchiveType($source);
        $type = 'zip';
        if ($archive_type == 'tar.gz' || $archive_type == 'tgz') {
            $type = 'tar.gz';
        } elseif ($archive_type == 'tar') {
            $type = 'tar';
        }

        $result = $this->unzipArchive($target, basename($source), $type);

        if ($result && isset($result['status']) && ($result['status'] === true || $result['status'] === 'true' || $result['status'] === 1)) {
            return array('success' => true, 'msg' => '解压成功');
        } else {
            $error_msg = isset($result['msg']) ? $result['msg'] : '解压失败';
            return array('success' => false, 'msg' => $error_msg);
        }
    }

    /**
     * 写入配置文件
     * @param string $config_file 配置文件路径
     * @param string $content 文件内容
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function writeConfigFile($config_file, $content) {

        // 确保目录存在
        $dir = dirname($config_file);
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }

        // 写入文件
        $result = file_put_contents($config_file, $content);

        if ($result !== false) {
            return array('success' => true, 'msg' => '配置文件写入成功');
        } else {
            return array('success' => false, 'msg' => '配置文件写入失败');
        }
    }

    /**
     * 执行命令
     * @param string $command 要执行的命令
     * @return array 结果数组 ['success' => bool, 'msg' => string]
     */
    private function executeCommand($command) {

        // 使用宝塔API执行命令
        $url = $this->bt_api->BT_PANEL . '/system?action=ExecShell';
        $p_data = $this->bt_api->GetKeyData();
        $p_data['shell'] = $command;

        $result = $this->bt_api->HttpPostCookie($url, $p_data);
        $response = json_decode($result, true);

        if ($response && isset($response['status']) && ($response['status'] === true || $response['status'] === 'true' || $response['status'] === 1)) {
            return array('success' => true, 'msg' => '命令执行成功');
        } else {
            $error_msg = isset($response['msg']) ? $response['msg'] : '命令执行失败';
            return array('success' => false, 'msg' => $error_msg);
        }
    }

    public function purchaseProgram($programId) {
        global $DB;
        $id = intval($programId);
        if ($id <= 0) {
            $this->error('参数错误');
        }

        $id_escaped = $DB->escape($id);
        $program = $DB->get_row("SELECT * FROM `bs` WHERE `id`='{$id_escaped}'");
        if (!$program) {
            $this->error('程序不存在');
        }

        $price = floatval($program['jg']);
        $uid = isset($this->user['id']) ? intval($this->user['id']) : 0;

        // 检查是否已购买
        $uid_escaped = $DB->escape($uid);
        $id_escaped = $DB->escape($id);
        $purchased = $DB->get_row("SELECT * FROM `bs_purchase` WHERE `uid`='{$uid_escaped}' AND `bs_id`='{$id_escaped}' AND `status`=1");
        if ($purchased) {
            $this->error('您已购买过此程序，无需重复购买');
        }

        // 免费程序直接标记为已购买
        if ($price <= 0) {
            $uid_escaped = $DB->escape($uid);
        $id_escaped = $DB->escape($id);
        $DB->query("INSERT INTO `bs_purchase` (`uid`, `bs_id`, `price`, `pay_time`, `status`) VALUES ('{$uid_escaped}', '{$id_escaped}', '0.00', NOW(), 1)");
            $this->success(array('msg' => '获取成功'));
            return;
        }

        // 检查用户余额
        $uid_escaped = $DB->escape($uid);
        $user = $DB->get_row("SELECT * FROM `zj` WHERE `id`='{$uid_escaped}'");
        if (!$user) {
            $this->error('用户信息不存在');
        }

        $balance = floatval($user['balance']);
        if ($balance < $price) {
            $msg = '余额不足，当前余额: ¥' . number_format($balance, 2) . '，需要: ¥' . number_format($price, 2);
            exit(json_encode(array('status' => false, 'code' => 'insufficient_balance', 'msg' => $msg)));
        }

        // 扣除余额
        $new_balance = $balance - $price;
        $new_balance_escaped = $DB->escape($new_balance);
        $uid_escaped = $DB->escape($uid);
        $DB->query("UPDATE `zj` SET `balance`='{$new_balance_escaped}' WHERE `id`='{$uid_escaped}'");

        // 记录购买
        $uid_escaped = $DB->escape($uid);
        $id_escaped = $DB->escape($id);
        $price_escaped = $DB->escape($price);
        $DB->query("INSERT INTO `bs_purchase` (`uid`, `bs_id`, `price`, `pay_time`, `status`) VALUES ('{$uid_escaped}', '{$id_escaped}', '{$price_escaped}', NOW(), 1)");

        // 记录余额变动
        $uid_escaped = $DB->escape($uid);
        $price_escaped = $DB->escape($price);
        $new_balance_escaped = $DB->escape($new_balance);
        $program_name_escaped = $DB->escape($program['name']);
        $DB->query("INSERT INTO `balance_logs` (`uid`, `type`, `amount`, `balance`, `description`, `create_time`) VALUES ('{$uid_escaped}', 'deduct', '{$price_escaped}', '{$new_balance_escaped}', '购买程序: {$program_name_escaped}', NOW())");

        $this->log('购买程序', '购买程序: ' . $program['name'] . ', 价格: ¥' . $price);
        $this->success(array('msg' => '购买成功'));
    }
}
?>