<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Acemcp Management</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body class="bg-gray-100">
    <div x-data="mcpManager()" x-init="init()" class="container mx-auto p-6">
        <header class="bg-white shadow rounded-lg p-6 mb-6 flex justify-between items-center">
            <div>
                <h1 class="text-3xl font-bold text-gray-800" x-text="t('title')"></h1>
                <p class="text-gray-600 mt-2" x-text="t('subtitle')"></p>
            </div>
            <div class="flex gap-2">
                <button @click="setLanguage('en')" :class="lang === 'en' ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-700'" class="px-3 py-1 rounded text-sm">
                    English
                </button>
                <button @click="setLanguage('zh')" :class="lang === 'zh' ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-700'" class="px-3 py-1 rounded text-sm">
                    中文
                </button>
            </div>
        </header>

        <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
            <div class="bg-white shadow rounded-lg p-6">
                <h3 class="text-lg font-semibold text-gray-700 mb-2" x-text="t('status')"></h3>
                <p class="text-2xl font-bold" :class="status.status === 'running' ? 'text-green-600' : 'text-red-600'" x-text="t('status_' + status.status)"></p>
            </div>
            <div class="bg-white shadow rounded-lg p-6">
                <h3 class="text-lg font-semibold text-gray-700 mb-2" x-text="t('indexed_projects')"></h3>
                <p class="text-2xl font-bold text-blue-600" x-text="status.project_count"></p>
            </div>
            <div class="bg-white shadow rounded-lg p-6">
                <h3 class="text-lg font-semibold text-gray-700 mb-2" x-text="t('storage_path')"></h3>
                <p class="text-sm text-gray-600 truncate" x-text="status.storage_path"></p>
            </div>
        </div>

        <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
            <div class="bg-white shadow rounded-lg p-6">
                <div class="flex justify-between items-center mb-4">
                    <h2 class="text-xl font-bold text-gray-800" x-text="t('configuration')"></h2>
                    <button @click="editMode = !editMode" class="px-3 py-1 bg-blue-500 text-white rounded hover:bg-blue-600 text-sm">
                        <span x-text="editMode ? t('cancel') : t('edit')"></span>
                    </button>
                </div>

                <div x-show="!editMode" class="space-y-3">
                    <div>
                        <label class="block text-sm font-medium text-gray-700" x-text="t('base_url')"></label>
                        <p class="mt-1 text-sm text-gray-900" x-text="config.base_url"></p>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700" x-text="t('token')"></label>
                        <p class="mt-1 text-sm text-gray-900" x-text="config.token"></p>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700" x-text="t('index_storage_path')"></label>
                        <p class="mt-1 text-sm text-gray-900" x-text="config.index_storage_path"></p>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700" x-text="t('max_index_files')"></label>
                        <p class="mt-1 text-sm text-gray-900" x-text="config.max_index_files"></p>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700" x-text="t('text_extensions')"></label>
                        <p class="mt-1 text-sm text-gray-900" x-text="config.text_extensions?.join(', ')"></p>
                    </div>
                </div>

                <form x-show="editMode" @submit.prevent="saveConfig()" class="space-y-3">
                    <div>
                        <label class="block text-sm font-medium text-gray-700" x-text="t('base_url')"></label>
                        <input type="text" x-model="editConfig.base_url" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-sm p-2 border">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700" x-text="t('token')"></label>
                        <input type="text" x-model="editConfig.token" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-sm p-2 border">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700" x-text="t('index_storage_path')"></label>
                        <input type="text" x-model="editConfig.index_storage_path" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-sm p-2 border">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700" x-text="t('max_index_files')"></label>
                        <input type="number" x-model.number="editConfig.max_index_files" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-sm p-2 border">
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700" x-text="t('text_extensions')"></label>
                        <textarea x-model="editConfig.text_extensions_str" rows="3" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-sm p-2 border" placeholder=".py, .js, .ts, .md"></textarea>
                        <p class="mt-1 text-xs text-gray-500" x-text="t('text_extensions_hint')"></p>
                    </div>
                    <div class="flex gap-2">
                        <button type="submit" class="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 text-sm" x-text="t('save_changes')">
                        </button>
                        <button type="button" @click="editMode = false" class="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600 text-sm" x-text="t('cancel')">
                        </button>
                    </div>
                    <p x-show="saveMessage" x-text="saveMessage" class="text-sm" :class="saveSuccess ? 'text-green-600' : 'text-red-600'"></p>
                </form>
            </div>

            <div class="bg-white shadow rounded-lg p-6">
                <div class="flex justify-between items-center mb-4">
                    <h2 class="text-xl font-bold text-gray-800" x-text="t('realtime_logs')"></h2>
                    <button @click="clearLogs()" class="px-3 py-1 bg-red-500 text-white rounded hover:bg-red-600 text-sm" x-text="t('clear')">
                    </button>
                </div>
                <div class="bg-gray-900 text-green-400 p-4 rounded font-mono text-xs h-96 overflow-y-auto" id="log-container">
                    <template x-for="log in logs" :key="log.id">
                        <div x-text="log.text" class="mb-1"></div>
                    </template>
                    <template x-if="logs.length === 0">
                        <div class="text-gray-500" x-text="t('waiting_logs')"></div>
                    </template>
                </div>
                <div class="mt-2 text-sm text-gray-600">
                    <span :class="wsConnected ? 'text-green-600' : 'text-red-600'">
                        ● <span x-text="wsConnected ? t('connected') : t('disconnected')"></span>
                    </span>
                </div>
            </div>
        </div>
    </div>

    <script>
        const translations = {
            en: {
                title: 'Acemcp Management',
                subtitle: 'MCP Server for Codebase Indexing',
                status: 'Status',
                status_running: 'Running',
                status_loading: 'Loading',
                indexed_projects: 'Indexed Projects',
                storage_path: 'Storage Path',
                configuration: 'Configuration',
                edit: 'Edit',
                cancel: 'Cancel',
                base_url: 'Base URL',
                token: 'Token',
                index_storage_path: 'Index Storage Path',
                max_index_files: 'Max Index Files',
                text_extensions: 'Text Extensions',
                text_extensions_hint: 'Comma-separated list of file extensions (e.g., .py, .js, .ts)',
                save_changes: 'Save Changes',
                realtime_logs: 'Real-time Logs',
                clear: 'Clear',
                waiting_logs: 'Waiting for logs...',
                connected: 'Connected',
                disconnected: 'Disconnected'
            },
            zh: {
                title: 'Acemcp 管理',
                subtitle: '代码库索引 MCP 服务器',
                status: '状态',
                status_running: '运行中',
                status_loading: '加载中',
                indexed_projects: '已索引项目',
                storage_path: '存储路径',
                configuration: '配置',
                edit: '编辑',
                cancel: '取消',
                base_url: '基础 URL',
                token: '令牌',
                index_storage_path: '索引存储路径',
                max_index_files: '最大索引文件数',
                text_extensions: '文本扩展名',
                text_extensions_hint: '逗号分隔的文件扩展名列表（例如：.py, .js, .ts）',
                save_changes: '保存更改',
                realtime_logs: '实时日志',
                clear: '清空',
                waiting_logs: '等待日志...',
                connected: '已连接',
                disconnected: '已断开'
            }
        };

        function mcpManager() {
            return {
                lang: localStorage.getItem('lang') || 'en',
                status: {
                    status: 'loading',
                    project_count: 0,
                    storage_path: ''
                },
                config: {},
                editConfig: {},
                editMode: false,
                saveMessage: '',
                saveSuccess: false,
                logs: [],
                wsConnected: false,
                ws: null,
                logIdCounter: 0,

                t(key) {
                    return translations[this.lang][key] || key;
                },

                setLanguage(lang) {
                    this.lang = lang;
                    localStorage.setItem('lang', lang);
                },

                async init() {
                    await this.loadStatus();
                    await this.loadConfig();
                    this.connectWebSocket();
                    setInterval(() => this.loadStatus(), 5000);
                },

                async loadStatus() {
                    try {
                        const response = await fetch('/api/status');
                        this.status = await response.json();
                    } catch (error) {
                        console.error('Failed to load status:', error);
                    }
                },

                async loadConfig() {
                    try {
                        const response = await fetch('/api/config');
                        this.config = await response.json();
                        this.editConfig = {
                            base_url: this.config.base_url,
                            token: this.config.token_full || '',
                            index_storage_path: this.config.index_storage_path,
                            max_index_files: this.config.max_index_files,
                            text_extensions_str: this.config.text_extensions?.join(', ') || ''
                        };
                    } catch (error) {
                        console.error('Failed to load config:', error);
                    }
                },

                async saveConfig() {
                    try {
                        this.saveMessage = 'Saving...';
                        this.saveSuccess = true;

                        const payload = {
                            base_url: this.editConfig.base_url,
                            token: this.editConfig.token,
                            index_storage_path: this.editConfig.index_storage_path,
                            max_index_files: this.editConfig.max_index_files,
                            text_extensions: this.editConfig.text_extensions_str
                                .split(',')
                                .map(ext => ext.trim())
                                .filter(ext => ext.length > 0)
                        };

                        const response = await fetch('/api/config', {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json'
                            },
                            body: JSON.stringify(payload)
                        });

                        if (response.ok) {
                            const result = await response.json();
                            this.saveMessage = result.message;
                            this.saveSuccess = true;
                            await this.loadConfig();
                            await this.loadStatus();
                            setTimeout(() => {
                                this.editMode = false;
                                this.saveMessage = '';
                            }, 3000);
                        } else {
                            const error = await response.json();
                            this.saveMessage = error.detail || 'Failed to save configuration';
                            this.saveSuccess = false;
                        }
                    } catch (error) {
                        console.error('Failed to save config:', error);
                        this.saveMessage = 'Failed to save configuration: ' + error.message;
                        this.saveSuccess = false;
                    }
                },

                connectWebSocket() {
                    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
                    const wsUrl = `${protocol}//${window.location.host}/ws/logs`;
                    
                    this.ws = new WebSocket(wsUrl);
                    
                    this.ws.onopen = () => {
                        this.wsConnected = true;
                        console.log('WebSocket connected');
                    };
                    
                    this.ws.onmessage = (event) => {
                        this.logs.push({
                            id: this.logIdCounter++,
                            text: event.data
                        });
                        if (this.logs.length > 500) {
                            this.logs.shift();
                        }
                        this.$nextTick(() => {
                            const container = document.getElementById('log-container');
                            container.scrollTop = container.scrollHeight;
                        });
                    };
                    
                    this.ws.onclose = () => {
                        this.wsConnected = false;
                        console.log('WebSocket disconnected, reconnecting...');
                        setTimeout(() => this.connectWebSocket(), 3000);
                    };
                    
                    this.ws.onerror = (error) => {
                        console.error('WebSocket error:', error);
                    };
                },

                clearLogs() {
                    this.logs = [];
                }
            };
        }
    </script>
</body>
</html>

