{% extends 'base.html' %}
{% block title %}手动分析 - 天璇{% endblock %}
{% block content %}
<style>
    .step-card {
        border: 1px solid #e0e0e0; border-radius: 8px; padding: 1rem; margin: 0.75rem 0;
        background: #fafafa; transition: opacity 0.3s;
    }
    .step-card.executing { opacity: 0.6; pointer-events: none; }
    .step-card.done { border-left: 4px solid #43aa8b; }
    .step-card.failed { border-left: 4px solid #dc3545; }
    .step-result {
        background: #1a1a2e; color: #e0e0e0; padding: 0.75rem; border-radius: 6px;
        margin-top: 0.5rem; font-size: 0.8rem; max-height: 300px; overflow: auto;
        white-space: pre-wrap; display: none;
    }
    .step-header { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.5rem; }
    .step-num {
        background: #4361ee; color: #fff; border-radius: 50%; width: 24px; height: 24px;
        display: flex; align-items: center; justify-content: center; font-size: 0.8rem; font-weight: 700;
        flex-shrink: 0;
    }
    .step-num.done { background: #43aa8b; }
    .step-num.failed { background: #dc3545; }
    .step-name { font-weight: 600; font-size: 1rem; }
    .param-row { margin: 0.4rem 0; display: flex; align-items: center; gap: 0.5rem; }
    .param-row label { min-width: 120px; font-size: 0.85rem; color: #555; }
    .param-row input, .param-row select { flex: 1; padding: 0.35rem; border: 1px solid #ccc; border-radius: 4px; font-size: 0.85rem; }
    .remove-step { color: #dc3545; cursor: pointer; font-size: 1.2rem; margin-left: auto; padding:0 0.25rem; }
    .badge-tool {
        display: inline-block; padding: 0.1rem 0.4rem; border-radius: 3px;
        font-size: 0.7rem; font-weight: 600; margin-right: 0.3rem;
    }
    .badge-core { background: #4361ee; color: #fff; }
    .badge-analysis { background: #43aa8b; color: #fff; }
    .badge-diag { background: #f72585; color: #fff; }
    .save-load-bar { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
    .status-dot { display:inline-block; width:8px; height:8px; border-radius:50%; margin-right:4px; }
    .status-dot.pending { background:#ccc; }
    .status-dot.running { background:#4361ee; animation:pulse 0.8s infinite; }
    .status-dot.done { background:#43aa8b; }
    .status-dot.failed { background:#dc3545; }
    @keyframes pulse { 0%{opacity:1;} 50%{opacity:0.3;} 100%{opacity:1;} }
    .auto-plan-row { display:flex; align-items:center; gap:0.5rem; padding:0.3rem 0; font-size:0.85rem; }
    .auto-plan-row .plan-name { flex:1; }
    .pin-btn { cursor:pointer; font-size:1rem; border:none; background:none; padding:0 0.25rem; transition: transform 0.2s; }
    .pin-btn:hover { transform:scale(1.2); }
    .pin-btn.pinned { color:#f4a261; }
    .pin-btn.unpinned { color:#aaa; }
    #autoPlansList { max-height:180px; overflow-y:auto; margin-top:0.25rem; padding-top:0.25rem; border-top:1px dashed #eee; }
    /* Dataset selector table */
    .ds-table { width:100%; border-collapse:collapse; font-size:0.85rem; }
    .ds-table th { background:#f5f5f5; padding:0.4rem 0.5rem; text-align:left; font-weight:600; border-bottom:2px solid #ddd; position:sticky; top:0; }
    .ds-table td { padding:0.35rem 0.5rem; border-bottom:1px solid #eee; }
    .ds-table tr { cursor:pointer; transition: background 0.15s; }
    .ds-table tr:hover { background:#e8f0fe; }
    .ds-table tr.selected { background:#d4e6ff; font-weight:600; }
    .status-badge { display:inline-block; padding:0.15rem 0.5rem; border-radius:3px; font-size:0.75rem; font-weight:600; }
    .status-badge.ready { background:#e8f5e9; color:#2e7d32; }
    .status-badge.analyzing { background:#fff3e0; color:#e65100; }
    .status-badge.completed { background:#e3f2fd; color:#1565c0; }
    .status-badge.failed { background:#ffebee; color:#c62828; }
    .filter-btn { padding:0.3rem 0.75rem; border:1px solid #ccc; border-radius:4px; background:#fafafa; cursor:pointer; font-size:0.8rem; transition: all 0.15s; }
    .filter-btn.active { background:#4361ee; color:#fff; border-color:#4361ee; }
    .filter-btn:hover:not(.active) { background:#e8e8e8; }
</style>

<div class="card">
    <h2>🔧 手动分析工作台</h2>
    <p style="color:#666;">构建分析方案（带参数的执行流）→ 保存/加载 → 逐步或一键运行</p>
    <!-- Save/Load bar -->
    <div class="save-load-bar" style="margin-top:0.75rem;padding-top:0.75rem;border-top:1px solid #eee;">
        <input type="text" id="planName" placeholder="方案名称" style="padding:0.35rem;border:1px solid #ccc;border-radius:4px;width:200px;">
        <button class="btn" onclick="savePlan()" style="background:#43aa8b;color:#fff;font-size:0.85rem;">💾 保存</button>
        <select id="loadPlanSelect" style="padding:0.35rem;border:1px solid #ccc;border-radius:4px;">
            <option value="">-- 加载方案 --</option>
        </select>
        <button class="btn" onclick="loadPlan()" style="font-size:0.85rem;">📂 加载</button>
        <button class="btn" onclick="deletePlan()" style="background:#dc3545;color:#fff;font-size:0.85rem;">🗑 删除</button>
    </div>
    <div id="autoPlansList"></div>
</div>

<!-- Dataset selector with status badges -->
<div class="card" id="datasetSelectorCard">
    <h3>📁 选择数据集</h3>
    <div style="display:flex;gap:0.5rem;margin-bottom:0.75rem;" id="statusFilter">
        <button class="btn filter-btn" data-filter="all">全部</button>
        <button class="btn filter-btn active" data-filter="ready">待分析</button>
        <button class="btn filter-btn" data-filter="completed">已分析</button>
        <button class="btn filter-btn" data-filter="failed">错误</button>
    </div>
    <div id="datasetTable" style="max-height:300px;overflow-y:auto;border:1px solid #e0e0e0;border-radius:4px;">
        <p style="text-align:center;color:#999;padding:1rem;">加载中...</p>
    </div>
</div>

<!-- Dataset selector + Run All (keeping dropdown hidden as data source) -->
<div class="card">
    <div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap;">
        <div style="flex:1;min-width:200px;">
            <label style="font-weight:600;font-size:0.9rem;">📁 数据集</label>
            <select id="datasetSelect" onchange="onDatasetChange()" style="display:none;">
                <option value="">-- 选择 --</option>
        {% for ds in datasets %}
        <option value="{{ ds.dataset_id }}">{{ ds.dataset_id }} ({{ ds.row_count|default:"?" }} rows, {{ ds.file_count|default:"?" }} files)</option>
        {% endfor %}
            </select>
        </div>
        <button class="btn btn-primary" id="runAllBtn" onclick="runAll()" style="margin-top:1rem;">▶▶ 全部执行</button>
    </div>
</div>

<!-- Filter builder card -->
<div class="card" id="filterCard" style="background:#f0f4ff;border-color:#c8d6e5;">
    <h3>🔍 数据筛选</h3>
    <div id="filterNoDataset" style="text-align:center;color:#999;padding:1rem;font-size:0.9rem;">选择数据集后可用</div>
    <div id="filterRows" style="display:none;">
        <div class="filter-row" data-idx="0" style="display:flex;gap:0.5rem;align-items:center;margin-bottom:0.4rem;">
            <select class="filter-col" style="flex:1;padding:0.35rem;border:1px solid #ccc;border-radius:4px;"><option value="">-- 列 --</option></select>
            <select class="filter-op" style="width:130px;padding:0.35rem;border:1px solid #ccc;border-radius:4px;">
                <option value="eq">==</option><option value="neq">!=</option>
                <option value="gt">&gt;</option><option value="lt">&lt;</option>
                <option value="gte">&gt;=</option><option value="lte">&lt;=</option>
                <option value="contains">contains</option>
                <option value="is_null">is_null</option>
                <option value="not_null">not_null</option>
                <option value="in">in</option>
            </select>
            <input class="filter-val" placeholder="值" style="flex:1;padding:0.35rem;border:1px solid #ccc;border-radius:4px;">
            <button class="btn" onclick="removeFilterRow(this)" style="background:#dc3545;color:#fff;font-size:0.8rem;padding:0.2rem 0.5rem;display:none;">✕</button>
        </div>
    </div>
    <div id="filterControls" style="display:none;margin:0.5rem 0;gap:0.5rem;align-items:center;">
        <label style="font-size:0.85rem;white-space:nowrap;">逻辑:</label>
        <select id="filterLogic" style="padding:0.35rem;border:1px solid #ccc;border-radius:4px;">
            <option value="and">AND</option><option value="or">OR</option>
        </select>
        <button class="btn" onclick="addFilterRow()" style="font-size:0.8rem;background:#e8f0fe;color:#4361ee;">+ 添加条件</button>
        <button class="btn" onclick="resetFilter()" style="font-size:0.8rem;background:#fce4ec;color:#c62828;">重置</button>
        <button class="btn btn-primary" onclick="applyFilter()" style="margin-left:auto;">应用筛选</button>
    </div>
    <div id="filterResult" style="display:none;margin-top:0.5rem;padding:0.5rem;background:#e8f5e9;border-radius:4px;font-size:0.85rem;"></div>
</div>

<!-- Workflow builder -->
<div class="card">
    <h3>📋 分析步骤 <span style="font-weight:400;font-size:0.85rem;color:#888;">共 <span id="stepCount">0</span> 步</span></h3>
    <div id="workflowSteps"></div>
    <button class="btn" onclick="showToolPicker()" style="margin-top:0.5rem;background:#e8f0fe;color:#4361ee;">+ 添加步骤</button>
</div>

<!-- Tool picker modal -->
<div id="toolPicker" style="display:none;margin-top:1rem;">
    <div class="card">
        <h4>选择工具</h4>
        <div style="display:flex;gap:0.5rem;margin:0.5rem 0;flex-wrap:wrap;">
            <button class="btn" onclick="filterTools('core')" style="background:#4361ee;color:#fff;font-size:0.8rem;">⚡核心</button>
            <button class="btn" onclick="filterTools('analysis')" style="background:#43aa8b;color:#fff;font-size:0.8rem;">🔍分析</button>
            <button class="btn" onclick="filterTools('diag')" style="background:#f72585;color:#fff;font-size:0.8rem;">🩺诊断</button>
            <button class="btn" onclick="filterTools('all')" style="font-size:0.8rem;">全部</button>
        </div>
        <div id="toolList" style="max-height:300px;overflow-y:auto;"></div>
    </div>
</div>

<script>
const TOOLS = {{ tools_meta|safe }};
const CATEGORIES = {
    {% for t in core_tools %}'{{ t.name }}': 'core', {% endfor %}
    {% for t in analysis_tools %}'{{ t.name }}': 'analysis', {% endfor %}
    {% for t in diag_tools %}'{{ t.name }}': 'diag', {% endfor %}
};
let stepCounter = 0;
const workflow = [];
let running = false;

// ── Dataset selector with status badges ──
const DATASETS_WITH_STATUS = {{ datasets_with_status_json|safe }};
let activeFilter = 'ready';

function buildDatasetTable(filter) {
    const container = document.getElementById('datasetTable');
    let rows = DATASETS_WITH_STATUS.filter(ds => {
        if (filter === 'all') return true;
        if (filter === 'ready') return ds.run_status === 'ready' || (!ds.run_status || ds.run_status === 'ready');
        if (filter === 'completed') return ds.run_status === 'completed';
        if (filter === 'failed') return ds.run_status === 'failed';
        return true;
    });

    if (!rows.length) {
        container.innerHTML = '<p style="text-align:center;color:#999;padding:1rem;">无匹配的数据集</p>';
        return;
    }

    let html = '<table class="ds-table"><thead><tr><th>ID</th><th>状态</th><th>行数</th><th>文件数</th><th>SVD</th><th></th></tr></thead><tbody>';
    rows.forEach(ds => {
        const label = ds.status_label || '待分析';
        const badgeClass = (ds.run_status === 'completed') ? 'completed' :
                          (ds.run_status === 'failed') ? 'failed' :
                          (ds.run_status && ds.run_status !== 'ready') ? 'analyzing' : 'ready';
        const dispId = ds.display_id || ds.dataset_id;
        const rowCount = ds.row_count != null ? ds.row_count.toLocaleString() : '?';
        const fileCount = ds.file_count != null ? ds.file_count : '?';
        const svdInfo = ds.svd_components ? `SVD: ${ds.svd_components}维` : '-';
        html += `<tr data-ds-id="${ds.dataset_id}" onclick="selectDataset('${ds.dataset_id}')">
            <td style="font-weight:600;">${dispId}</td>
            <td><span class="status-badge ${badgeClass}">${label}</span></td>
            <td>${rowCount}</td>
            <td>${fileCount}</td>
            <td>${svdInfo}</td>
            <td><button class="btn" onclick="event.stopPropagation();selectDataset('${ds.dataset_id}')" style="font-size:0.75rem;padding:0.2rem 0.5rem;">选择</button></td>
        </tr>`;
    });
    html += '</tbody></table>';
    container.innerHTML = html;
}

function selectDataset(dsId) {
    const sel = document.getElementById('datasetSelect');
    let opt = Array.from(sel.options).find(o => o.value === dsId);
    if (!opt) {
        opt = document.createElement('option');
        opt.value = dsId;
        opt.text = dsId;
        sel.add(opt);
    }
    sel.value = dsId;
    onDatasetChange();

    // Highlight selected row
    document.querySelectorAll('#datasetTable tr').forEach(tr => tr.classList.remove('selected'));
    const row = document.querySelector(`#datasetTable tr[data-ds-id="${dsId}"]`);
    if (row) row.classList.add('selected');
}

function setDatasetFilter(filter) {
    activeFilter = filter;
    document.querySelectorAll('#statusFilter .filter-btn').forEach(btn => {
        btn.classList.toggle('active', btn.dataset.filter === filter);
    });
    buildDatasetTable(filter);
}

// Init filter tabs & build default table
document.querySelectorAll('#statusFilter .filter-btn').forEach(btn => {
    btn.addEventListener('click', function() {
        setDatasetFilter(this.dataset.filter);
    });
});
buildDatasetTable('ready');

// ── Tool picker ──
function showToolPicker() {
    document.getElementById('toolPicker').style.display = 'block';
    filterTools('all');
}
function filterTools(cat) {
    const list = document.getElementById('toolList');
    list.innerHTML = '';
    TOOLS.forEach(t => {
        const c = CATEGORIES[t.name] || 'other';
        if (cat !== 'all' && c !== cat) return;
        const badge = c === 'core' ? 'CORE' : c === 'analysis' ? 'ANALYSIS' : 'DIAG';
        const color = c === 'core' ? '#4361ee' : c === 'analysis' ? '#43aa8b' : '#f72585';
        const div = document.createElement('div');
        div.style.cssText = 'padding:0.5rem;cursor:pointer;border-bottom:1px solid #eee;display:flex;align-items:center;gap:0.5rem;';
        div.innerHTML = `<span style="background:${color};color:#fff;padding:0.1rem 0.4rem;border-radius:3px;font-size:0.7rem;font-weight:600;">${badge}</span>
                         <span style="font-weight:600;">${t.name}</span>
                         <span style="color:#888;font-size:0.8rem;flex:1;">${t.description.substring(0,80)}...</span>`;
        div.onclick = () => addStep(t);
        list.appendChild(div);
    });
}

// ── Workflow steps ──
function addStep(tool, savedParams) {
    document.getElementById('toolPicker').style.display = 'none';
    const idx = stepCounter++;
    const step = { idx, tool, params: savedParams || {}, status: 'pending' };
    workflow.push(step);
    renderSteps();
}
function removeStep(idx) {
    const pos = workflow.findIndex(s => s.idx === idx);
    if (pos >= 0) workflow.splice(pos, 1);
    renderSteps();
}
function moveStep(idx, dir) {
    const pos = workflow.findIndex(s => s.idx === idx);
    if (pos < 0) return;
    const target = pos + dir;
    if (target < 0 || target >= workflow.length) return;
    [workflow[pos], workflow[target]] = [workflow[target], workflow[pos]];
    renderSteps();
}

function buildParamForm(tool, container, stepIdx, savedParams) {
    const props = tool.inputSchema && tool.inputSchema.properties;
    if (!props) { container.innerHTML = '<p style="color:#888;font-size:0.85rem;">无需参数</p>'; return; }
    const required = tool.inputSchema.required || [];
    let html = '';
    for (const [key, prop] of Object.entries(props)) {
        const isReq = required.includes(key);
        const label = prop.description || key;
        const saved = savedParams && savedParams[key] !== undefined ? savedParams[key] : null;
        let input;
        if (prop.type === 'boolean') {
            const val = saved !== null ? saved : true;
            input = `<select class="p-${stepIdx}" data-key="${key}"><option value="true" ${val===true?'selected':''}>true</option><option value="false" ${val===false?'selected':''}>false</option></select>`;
        } else if (prop.type === 'array') {
            const val = saved !== null ? JSON.stringify(saved) : '';
            input = `<input class="p-${stepIdx}" data-key="${key}" value='${val}' placeholder='["val1","val2"]' style="width:100%;">`;
        } else if (prop.type === 'number' || prop.type === 'integer') {
            const val = saved !== null ? saved : (prop.default || '');
            input = `<input class="p-${stepIdx}" data-key="${key}" type="number" value="${val}" step="${prop.type==='integer'?1:0.01}" style="width:100%;">`;
        } else {
            const val = saved !== null ? saved : '';
            if (key.includes('dataset_id')) {
                let opts = '<option value="">-- select --</option>';
                document.querySelectorAll('#datasetSelect option').forEach(o => { if (o.value) opts += `<option value="${o.value}" ${o.value===val?'selected':''}>${o.value}</option>`; });
                input = `<select class="p-${stepIdx}" data-key="${key}" style="width:100%;">${opts}</select>`;
            } else if (key === 'algorithm') {
                input = `<select class="p-${stepIdx}" data-key="${key}" style="width:100%;"><option value="hdbscan" ${val==='hdbscan'?'selected':''}>HDBSCAN</option><option value="kmeans" ${val==='kmeans'?'selected':''}>KMeans</option></select>`;
            } else if (key === 'method') {
                input = `<select class="p-${stepIdx}" data-key="${key}" style="width:100%;"><option value="zscore" ${val==='zscore'?'selected':''}>zscore</option><option value="anova" ${val==='anova'?'selected':''}>anova</option></select>`;
            } else {
                input = `<input class="p-${stepIdx}" data-key="${key}" value="${val}" placeholder="${label}" style="width:100%;">`;
            }
        }
        html += `<div class="param-row"><label>${key}${isReq?' <span style="color:#dc3545;">*</span>':''}</label>${input}</div>`;
    }
    container.innerHTML = html;
}

function collectParams(stepIdx) {
    const params = {};
    document.querySelectorAll(`.p-${stepIdx}`).forEach(el => {
        let val = el.value; const key = el.dataset.key;
        if (!key || val === '' || val === null) return;
        if (el.type === 'number') val = parseFloat(val);
        else if (val.startsWith('[') || val.startsWith('{')) { try { val = JSON.parse(val); } catch(e) {} }
        params[key] = val;
    });
    return params;
}

// ── Execute single step ──
async function executeStep(idx) {
    const step = workflow.find(s => s.idx === idx);
    if (!step) return;
    step.status = 'running'; renderSteps();
    const card = document.getElementById(`step-${idx}`);
    const resultDiv = document.getElementById(`result-${idx}`);
    resultDiv.style.display = 'none';
    const params = collectParams(idx);

    // Raw row-level clustering: skip entity aggregation entirely
    if (step.tool.name === 'build_entity_profiles') {
        step.status = 'done';
        resultDiv.textContent = '[已跳过] 逐行直接聚类模式，无需实体聚合';
        resultDiv.style.display = 'block';
        renderSteps();
        return;
    }
    // Always cluster raw rows directly
    if (step.tool.name === 'run_clustering') {
        params.cluster_mode = 'raw';
    }

    try {
        const resp = await fetch('/tools/run/', {
            method: 'POST',
            headers: { 'Content-Type':'application/json', 'X-CSRFToken':'{{ csrf_token }}' },
            body: JSON.stringify({ tool: step.tool.name, params }),
        });
        const data = await resp.json();
        resultDiv.textContent = JSON.stringify(data, null, 2);
        resultDiv.style.display = 'block';
        step.status = data.error ? 'failed' : 'done';
        // update dataset select from result
        if (data && data.result && data.result.dataset_id) {
            const sel = document.getElementById('datasetSelect');
            if (!Array.from(sel.options).some(o => o.value === data.result.dataset_id)) {
                const opt = document.createElement('option');
                opt.value = data.result.dataset_id;
                opt.text = data.result.dataset_id;
                sel.add(opt);
            }
        }
    } catch(err) {
        resultDiv.textContent = 'Error: ' + err.message;
        resultDiv.style.display = 'block';
        step.status = 'failed';
        showError('步骤 ' + step.tool.name + ' 执行失败: ' + err.message);
    }
    renderSteps();
}

// ── Run All (sequential) ──
async function runAll() {
    if (running) return;
    running = true;
    document.getElementById('runAllBtn').disabled = true;
    for (const step of workflow) {
        if (step.status === 'done') continue;  // skip already-done
        await executeStep(step.idx);
    }
    running = false;
    document.getElementById('runAllBtn').disabled = false;
}

// ── Save / Load ──
async function savePlan() {
    const name = document.getElementById('planName').value.trim();
    if (!name) { showError('请输入方案名称'); return; }
    const steps = workflow.map(s => ({
        tool_name: s.tool.name,
        params: collectParams(s.idx),
    }));
    try {
        const resp = await fetch('/tools/plan/', {
            method: 'POST',
            headers: { 'Content-Type':'application/json', 'X-CSRFToken':'{{ csrf_token }}' },
            body: JSON.stringify({ name, steps }),
        });
        const data = await resp.json();
        if (data.status === 'saved') {
            showSuccess('方案已保存: ' + name);
            loadPlanList();
        } else {
            showError('保存失败: ' + (data.error || ''));
        }
    } catch (e) { showError('保存方案失败: ' + e.message); }
}

async function loadPlanList() {
    try {
        const resp = await fetch('/tools/plan/');
        const data = await resp.json();
        const sel = document.getElementById('loadPlanSelect');
        sel.innerHTML = '<option value="">-- 加载方案 --</option>';
        const autoPlans = [];
        const regularPlans = [];
        (data.plans || []).forEach(p => {
            if (p.auto) {
                autoPlans.push(p);
                // Show in select with [自动] prefix
                const opt = document.createElement('option');
                opt.value = p.name; opt.text = '[自动] ' + p.name + (p.steps ? ' (' + p.steps + ' steps)' : '');
                sel.appendChild(opt);
            } else {
                regularPlans.push(p);
                const opt = document.createElement('option');
                opt.value = p.name; opt.text = p.name + (p.steps ? ' (' + p.steps + ' steps)' : '');
                sel.appendChild(opt);
            }
        });
        // Render auto plans list with pin buttons
        renderAutoPlans(autoPlans);
    } catch (e) { showError('加载方案列表失败: ' + e.message); }
}

function renderAutoPlans(autoPlans) {
    const container = document.getElementById('autoPlansList');
    if (!autoPlans.length) { container.innerHTML = ''; return; }
    let html = '<div style="font-weight:600;font-size:0.85rem;margin-bottom:0.25rem;color:#888;">📋 自动方案</div>';
    autoPlans.forEach(p => {
        const icon = p.pinned ? '📌' : '📍';
        const cssClass = p.pinned ? 'pinned' : 'unpinned';
        const planName = p.name.replace(/^_auto_/, '');
        html += `<div class="auto-plan-row">
            <span class="plan-name">[自动] ${planName} (${p.steps} steps)</span>
            <span class="pin-btn ${cssClass}" onclick="togglePin('${p.name}', ${!p.pinned})" title="${p.pinned ? '取消固定' : '固定'}">${icon}</span>
        </div>`;
    });
    container.innerHTML = html;
}

async function togglePin(name, newPinned) {
    try {
        const resp = await fetch('/tools/plan/', {
            method: 'POST',
            headers: { 'Content-Type':'application/json', 'X-CSRFToken':'{{ csrf_token }}' },
            body: JSON.stringify({ name, pin: newPinned }),
        });
        const data = await resp.json();
        if (data.status === 'pinned' || data.status === 'unpinned') {
            showSuccess(newPinned ? '已固定: ' + name : '已取消固定: ' + name);
            loadPlanList();  // Refresh
        } else {
            showError('操作失败: ' + (data.error || ''));
        }
    } catch (e) { showError('操作失败: ' + e.message); }
}

async function loadPlan() {
    const name = document.getElementById('loadPlanSelect').value;
    if (!name) return;
    try {
        const resp = await fetch('/tools/plan/?name=' + encodeURIComponent(name));
        const data = await resp.json();
        if (data.error) { showError(data.error); return; }
        // Clear current workflow
        workflow.length = 0;
        // Load steps
        for (const s of data.steps || []) {
            const tool = TOOLS.find(t => t.name === s.tool_name);
            if (tool) addStep(tool, s.params || {});
        }
        document.getElementById('planName').value = name;
    } catch (e) { showError('加载方案失败: ' + e.message); }
}

async function deletePlan() {
    const name = document.getElementById('loadPlanSelect').value;
    if (!name || !confirm('删除方案 "' + name + '" ?')) return;
    try {
        const resp = await fetch('/tools/plan/?name=' + encodeURIComponent(name), { method: 'DELETE' });
        const data = await resp.json();
        if (data.status === 'deleted') { showSuccess('已删除方案: ' + name); loadPlanList(); }
        else { showError('删除方案失败: ' + (data.error || '')); }
    } catch (e) { showError('删除方案失败: ' + e.message); }
}

// ── Render ──
function renderSteps() {
    const container = document.getElementById('workflowSteps');
    container.innerHTML = '';
    workflow.forEach((step, i) => {
        const c = CATEGORIES[step.tool.name] || 'other';
        const color = c==='core' ? '#4361ee' : c==='analysis' ? '#43aa8b' : '#f72585';
        const badge = c==='core' ? 'CORE' : c==='analysis' ? 'ANALYSIS' : 'DIAG';
        const statusColor = step.status==='done' ? '#43aa8b' : step.status==='failed' ? '#dc3545' : step.status==='running' ? '#4361ee' : '#ccc';
        const statusText = step.status==='done' ? '✓' : step.status==='failed' ? '✗' : step.status==='running' ? '⟳' : '';
        const statusPulse = step.status==='running' ? 'animation:pulse 0.8s infinite;' : '';

        const div = document.createElement('div');
        div.className = 'step-card' + (step.status==='done' ? ' done' : step.status==='failed' ? ' failed' : '');
        div.id = `step-${step.idx}`;
        div.innerHTML = `
            <div class="step-header">
                <span class="step-num ${step.status}" style="${statusPulse}">${statusText || (i+1)}</span>
                <span class="step-name">${step.tool.name}</span>
                <span class="badge-tool" style="background:${color};">${badge}</span>
                <span style="font-size:0.8rem;color:#888;flex:1;">${step.tool.description.substring(0,50)}</span>
                <span style="cursor:pointer;color:#888;font-size:0.9rem;" onclick="moveStep(${step.idx},-1)" title="上移">↑</span>
                <span style="cursor:pointer;color:#888;font-size:0.9rem;" onclick="moveStep(${step.idx},1)" title="下移">↓</span>
                <span class="remove-step" onclick="removeStep(${step.idx})">✕</span>
            </div>
            <div id="params-${step.idx}"></div>
            <button class="btn btn-primary" onclick="executeStep(${step.idx})" style="font-size:0.85rem;padding:0.35rem 0.75rem;margin-top:0.3rem;">▶ 执行</button>
            <div class="step-result" id="result-${step.idx}"></div>
        `;
        container.appendChild(div);
        buildParamForm(step.tool, document.getElementById(`params-${step.idx}`), step.idx, step.params);
    });
    document.getElementById('stepCount').textContent = workflow.length;
}

// ── Init: load plan list ──
loadPlanList();

// ── Dataset columns map from server ──
const DATASET_COLUMNS = {
    {% for ds in datasets %}
    '{{ ds.dataset_id|escapejs }}': {{ ds.columns|safe }},
    {% endfor %}
};

// ── Filter builder ──
function onDatasetChange() {
    const dsId = document.getElementById('datasetSelect').value;
    const noDatasetMsg = document.getElementById('filterNoDataset');
    const filterRows = document.getElementById('filterRows');
    const filterControls = document.getElementById('filterControls');
    const filterResult = document.getElementById('filterResult');

    if (!dsId) {
        // No dataset selected: show placeholder, hide filter UI
        noDatasetMsg.style.display = 'block';
        filterRows.style.display = 'none';
        filterControls.style.display = 'none';
        filterResult.style.display = 'none';
        // Clear column dropdowns
        document.querySelectorAll('.filter-col').forEach(sel => {
            sel.innerHTML = '<option value="">-- 请先选择数据集 --</option>';
        });
        return;
    }

    // Dataset selected: show filter UI, hide placeholder
    noDatasetMsg.style.display = 'none';
    filterRows.style.display = 'block';
    filterControls.style.display = 'flex';

    // Populate column dropdowns
    const cols = DATASET_COLUMNS[dsId] || [];
    document.querySelectorAll('.filter-col').forEach(sel => {
        const cur = sel.value;
        sel.innerHTML = '<option value="">-- 列 --</option>';
        cols.forEach(c => { sel.innerHTML += '<option value="' + c + '" ' + (c===cur?'selected':'') + '>' + c + '</option>'; });
    });
}

function addFilterRow() {
    const rows = document.querySelectorAll('.filter-row');
    if (rows.length >= 5) return; // max 5 rows
    const template = rows[0].cloneNode(true);
    template.querySelector('.filter-val').value = '';
    template.querySelector('.filter-col').selectedIndex = 0;
    template.querySelector('.filter-op').selectedIndex = 0;
    // Show remove button
    template.querySelector('button').style.display = 'inline-block';
    document.getElementById('filterRows').appendChild(template);
    onDatasetChange(); // repopulate columns
}

function removeFilterRow(btn) {
    const rows = document.querySelectorAll('.filter-row');
    if (rows.length <= 1) return;
    btn.closest('.filter-row').remove();
}

function resetFilter() {
    // Remove all rows except the first, then reset the first row
    const rows = document.querySelectorAll('.filter-row');
    for (let i = rows.length - 1; i >= 1; i--) {
        rows[i].remove();
    }
    const firstRow = rows[0];
    firstRow.querySelector('.filter-val').value = '';
    firstRow.querySelector('.filter-col').selectedIndex = 0;
    firstRow.querySelector('.filter-op').selectedIndex = 0;
    firstRow.querySelector('button').style.display = 'none';
    document.getElementById('filterResult').style.display = 'none';
}

function collectFilters() {
    const filters = [];
    document.querySelectorAll('.filter-row').forEach(row => {
        const col = row.querySelector('.filter-col').value;
        const op = row.querySelector('.filter-op').value;
        let val = row.querySelector('.filter-val').value;
        if (!col || !op) return;
        if ((op === 'in') && val) {
            val = val.split(',').map(s => s.trim()).filter(s => s);
        }
        filters.push({ column: col, op: op, value: val });
    });
    return filters;
}

async function applyFilter() {
    const dsId = document.getElementById('datasetSelect').value;
    if (!dsId) return;
    const filters = collectFilters();
    if (filters.length === 0) { showError('请添加至少一个筛选条件'); return; }
    const logic = document.getElementById('filterLogic').value;

    try {
        const resp = await fetch('/tools/filter/', {
            method: 'POST',
            headers: { 'Content-Type':'application/json', 'X-CSRFToken':'{{ csrf_token }}' },
            body: JSON.stringify({ dataset_id: dsId, filters, logic }),
        });
        const data = await resp.json();
        if (data.error) { showError('筛选失败: ' + data.error); return; }

        // Add filtered dataset to select
        const sel = document.getElementById('datasetSelect');
        const opt = document.createElement('option');
        opt.value = data.dataset_id;
        opt.text = data.dataset_id + ' (' + data.row_count + ' rows)';
        sel.add(opt);
        sel.value = data.dataset_id;
        onDatasetChange();

        const resultDiv = document.getElementById('filterResult');
        resultDiv.style.display = 'block';
        resultDiv.innerHTML = '<strong>✅ 筛选完成</strong>: 数据集 <code>' + data.dataset_id + '</code>，共 <strong>' + data.row_count + '</strong> 行';
        showSuccess('筛选完成: ' + data.row_count + ' 行');
    } catch(e) {
        showError('筛选请求失败: ' + e.message);
    }
}

// ── URL parameter: auto-select dataset ──
(function() {
    const params = new URLSearchParams(window.location.search);
    const dsParam = params.get('dataset');
    if (dsParam) {
        const sel = document.getElementById('datasetSelect');
        for (let i = 0; i < sel.options.length; i++) {
            if (sel.options[i].value === dsParam) {
                sel.selectedIndex = i;
                onDatasetChange();
                return;
            }
        }
        // Dataset may exist in store but not in initial template list
        const opt = document.createElement('option');
        opt.value = dsParam;
        opt.text = dsParam;
        sel.add(opt);
        sel.value = dsParam;
        onDatasetChange();
    }
})();
</script>
{% endblock %}