<?php

namespace app\service\AdminConsole;

use RuntimeException;

class MaintenanceScriptService
{
    private const ARG_PATTERN = '/^--[A-Za-z0-9_]+(?:=[A-Za-z0-9._:@\/-]+)?$/';

    public function list(): array
    {
        $docs = new AdminDocService();
        $items = [];
        foreach ($this->definitions() as $definition) {
            $item = $definition;
            try {
                $doc = $docs->readScriptDoc($definition['doc_name']);
                $item['doc_title'] = $doc['title'];
                $item['doc_html'] = $doc['html'];
                $item['doc_content'] = $doc['content'];
            } catch (RuntimeException) {
                $item['doc_title'] = null;
                $item['doc_html'] = null;
                $item['doc_content'] = null;
            }
            $items[] = $item;
        }

        return $items;
    }

    public function describe(string $name): array
    {
        $definitions = $this->definitions();
        if (!isset($definitions[$name])) {
            throw new RuntimeException('Script is not allowed.');
        }

        foreach ($this->list() as $item) {
            if ($item['name'] === $name) {
                return $item;
            }
        }

        throw new RuntimeException('Script metadata not found.');
    }

    public function run(string $name, array $args = []): array
    {
        $definitions = $this->definitions();
        if (!isset($definitions[$name])) {
            throw new RuntimeException('Script is not allowed.');
        }

        $script = $definitions[$name];
        $scriptPath = base_path('scripts/' . $script['file']);
        if (!is_file($scriptPath)) {
            throw new RuntimeException('Script file not found.');
        }

        $safeArgs = [];
        foreach ($args as $arg) {
            $arg = trim((string) $arg);
            if ($arg === '') {
                continue;
            }
            if (!preg_match(self::ARG_PATTERN, $arg)) {
                throw new RuntimeException('Only --key=value style arguments are allowed.');
            }
            $safeArgs[] = $arg;
        }

        $command = array_merge([PHP_BINARY, $scriptPath], $safeArgs);
        $descriptors = [
            0 => ['pipe', 'r'],
            1 => ['pipe', 'w'],
            2 => ['pipe', 'w'],
        ];

        $process = proc_open($command, $descriptors, $pipes, base_path());
        if (!is_resource($process)) {
            throw new RuntimeException('Failed to start script process.');
        }

        fclose($pipes[0]);
        $stdout = (string) stream_get_contents($pipes[1]);
        fclose($pipes[1]);
        $stderr = (string) stream_get_contents($pipes[2]);
        fclose($pipes[2]);
        $exitCode = proc_close($process);

        return [
            'script_name' => $name,
            'command' => array_merge(['php', 'scripts/' . $script['file']], $safeArgs),
            'exit_code' => $exitCode,
            'stdout' => $stdout,
            'stderr' => $stderr,
            'ok' => $exitCode === 0,
        ];
    }

    private function definitions(): array
    {
        return [
            'setup_database' => [
                'name' => 'setup_database',
                'file' => 'setup_database.php',
                'label' => '初始化数据库',
                'description' => '创建或补齐 archives、chunks 相关表结构与索引。',
                'doc_name' => 'setup_database.md',
                'args_hint' => '无参数',
            ],
            'setup_opensearch' => [
                'name' => 'setup_opensearch',
                'file' => 'setup_opensearch.php',
                'label' => '初始化 OpenSearch',
                'description' => '创建或补齐 proofdb_chunks 索引与 mapping。',
                'doc_name' => 'setup_opensearch.md',
                'args_hint' => '无参数',
            ],
            'reembed_chunks' => [
                'name' => 'reembed_chunks',
                'file' => 'reembed_chunks.php',
                'label' => '重新生成向量',
                'description' => '对 chunks 重新执行 embedding，支持 resume 与 --reset。',
                'doc_name' => 'reembed_chunks.md',
                'args_hint' => '--archive_uid=01... 或 --reset',
            ],
            'reindex_opensearch' => [
                'name' => 'reindex_opensearch',
                'file' => 'reindex_opensearch.php',
                'label' => '重建 OpenSearch 索引',
                'description' => '把 PostgreSQL 中已向量化的数据重新写入 OpenSearch。',
                'doc_name' => 'reindex_opensearch.md',
                'args_hint' => '--archive_uid=01...',
            ],
            'setup_admin_users' => [
                'name' => 'setup_admin_users',
                'file' => 'setup_admin_users.php',
                'label' => '初始化管理员用户',
                'description' => '创建 admin_users 表并写入或更新管理员账号。',
                'doc_name' => 'setup_admin_users.md',
                'args_hint' => '--username=admin --password=secret',
            ],
        ];
    }
}
