/* ─────────────────────────────────────────────────────────────
   11  P&ID 추출
───────────────────────────────────────────────────────────── */
let pidCurrentPage = 1;
let pidPageSize = 20;
let pidLastResult = null;  // Excel export용
let pidCatFilter = '';     // 카테고리 필터
let pidSortBy = '';        // 정렬 필드
let pidSortDesc = false;   // 내림차순

async function pidUpload() {
    const fileInput = document.getElementById('pid-file-input');
    const file = fileInput.files[0];
    const statusEl = document.getElementById('pid-upload-status');

    if (!file) {
        if (statusEl) statusEl.textContent = '❌ 파일을 선택하세요.';
        return;
    }
    const ext = file.name.toLowerCase().split('.').pop();
    if (!['dxf', 'pdf'].includes(ext)) {
        if (statusEl) statusEl.textContent = '❌ 지원 형식: .dxf, .pdf';
        return;
    }

    if (statusEl) statusEl.textContent = `📤 전송 중... (${(file.size / 1024).toFixed(0)} KB)`;

    try {
        const formData = new FormData();
        formData.append('file', file);

        const res = await fetch('/api/pid/upload', { method: 'POST', body: formData });
        if (!res.ok) {
            const err = await res.json().catch(() => ({ error: res.statusText }));
            throw new Error(err.error || res.statusText);
        }
        const data = await res.json();
        if (statusEl) statusEl.textContent = `✅ 전송 완료: ${data.fileName} (${(data.fileSize / 1024).toFixed(0)} KB)`;

        await pidLoadServerFiles(data.fileName);
    } catch (e) {
        if (statusEl) statusEl.textContent = `❌ 전송 실패: ${e.message}`;
    }
}

async function pidLoadServerFiles(selectFileName) {
    const sel = document.getElementById('pid-server-file');
    if (!sel) return;
    
    try {
        const res = await fetch('/api/pid/server-files');
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        
        // 응답이 JSON인지 확인
        const contentType = res.headers.get('content-type');
        if (!contentType || !contentType.includes('application/json')) {
            const text = await res.text();
            // HTML이 반환되면 P&ID 컨트롤러가 비활성화된 것
            if (text.startsWith('<!DOCTYPE') || text.startsWith('<html')) {
                throw new Error('P&ID 기능이 비활성화되어 있습니다. 관리자에게 문의하세요.');
            }
            throw new Error('예상치 못한 응답 형식');
        }
        
        const data = await res.json();

        if (!data.files || data.files.length === 0) {
            sel.innerHTML = '<option value="">-- 서버에 파일 없음 --</option>';
            return;
        }
        sel.innerHTML = data.files.map(f =>
            `<option value="${esc(f.fileName)}">${esc(f.fileName)} (${(f.fileSize / 1024).toFixed(0)} KB)</option>`
        ).join('');

        if (selectFileName) sel.value = selectFileName;
    } catch (e) {
        sel.innerHTML = `<option value="">오류: ${e.message}</option>`;
    }
}

// P&ID 추출 진행 상태 — 탭 전환 시 상태 초기화 방지
let pidExtracting = false;
let pidElapsedInterval = null;

async function pidExtract() {
    const fileName = document.getElementById('pid-server-file').value;
    const statusEl = document.getElementById('pid-status');
    const logEl = document.getElementById('pid-log');
    const elapsedEl = document.getElementById('pid-elapsed');

    if (!fileName) {
        if (statusEl) statusEl.textContent = '❌ 서버 파일을 선택하세요.';
        return;
    }

    pidExtracting = true;
    if (statusEl) statusEl.textContent = '추출 중...';
    if (logEl) {
        logEl.style.display = 'block';
        logEl.innerHTML = '';
    }

    // 경과 시간 시계 시작
    const startTime = Date.now();
    if (elapsedEl) {
        elapsedEl.style.display = 'inline';
        pidElapsedInterval = setInterval(() => {
            const seconds = Math.floor((Date.now() - startTime) / 1000);
            const m = String(Math.floor(seconds / 60)).padStart(2, '0');
            const s = String(seconds % 60).padStart(2, '0');
            elapsedEl.textContent = `${m}:${s}`;
        }, 1000);
    }

    try {
        const res = await fetch('/api/pid/extract', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ fileName, useImageMode: false })
        });

        if (!res.ok) {
            const err = await res.json().catch(() => ({ error: res.statusText }));
            throw new Error(err.error || res.statusText);
        }

        const data = await res.json();
        pidExtracting = false;
        const skipMsg = data.skippedDuplicates > 0 ? ` (${data.skippedDuplicates}건 중복 제외)` : '';
        if (statusEl) statusEl.textContent = `✅ 추출 완료: ${data.totalCount}건${skipMsg}`;
        log('pid-log', [
            { c: 'ok', t: `✅ 추출 완료: ${data.totalCount}건${skipMsg}` },
            { c: 'inf', t: `   신뢰도 70%+: ${data.confidenceItems}건` },
            { c: 'inf', t: `   신뢰도 50%미만: ${data.lowConfidenceItems}건` },
            ...(data.skippedDuplicates > 0 ? [{ c: 'warn', t: `   중복 스킵: ${data.skippedDuplicates}건` }] : [])
        ]);

        pidCurrentPage = 1;
        await pidLoadTable();
        pidUpdateStats();
    } catch (e) {
        pidExtracting = false;
        if (statusEl) statusEl.textContent = `❌ 오류: ${e.message}`;
        log('pid-log', [{ c: 'err', t: `❌ ${e.message}` }]);
    } finally {
        // 경과 시간 시계 종료
        if (pidElapsedInterval) {
            clearInterval(pidElapsedInterval);
            pidElapsedInterval = null;
        }
        if (elapsedEl) elapsedEl.style.display = 'none';
    }
}

function pidOnCatFilter() {
    pidCatFilter = document.getElementById('pid-cat-filter').value;
    pidCurrentPage = 1;
    pidLoadTable(1);
}

function pidToggleSort(field) {
    if (pidSortBy === field) {
        pidSortDesc = !pidSortDesc;
    } else {
        pidSortBy = field;
        pidSortDesc = false;
    }
    pidCurrentPage = 1;
    pidLoadTable(1);
}

async function pidLoadTable(page = 1) {
    pidCurrentPage = page;
    const container = document.getElementById('pid-table-container');
    const tbody = document.getElementById('pid-table-body');

    if (!container || !tbody) return;

    const catParam = pidCatFilter ? `&category=${encodeURIComponent(pidCatFilter)}` : '';
    const sortParam = pidSortBy ? `&sortBy=${pidSortBy}&sortDesc=${pidSortDesc}` : '';
    const si = document.getElementById('pid-sort-indicator');
    if (si) si.textContent = pidSortBy === 'tagName' ? (pidSortDesc ? '▼' : '▲') : '';
    tbody.innerHTML = '<tr><td colspan="14" style="text-align:center;padding:20px">로딩 중...</td></tr>';

    try {
        const res = await fetch(`/api/pid/equipment?page=${page}&pageSize=${pidPageSize}${catParam}${sortParam}`);
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        
        const data = await res.json();
        pidLastResult = data;
        
        if (!data.items || data.items.length === 0) {
            tbody.innerHTML = '<tr><td colspan="14" style="text-align:center;padding:20px">데이터가 없습니다</td></tr>';
            document.getElementById('pid-pagination').innerHTML = '';
            return;
        }
        
        const catOpts = ['','instrument','power_equipment','storage_equipment','process_equipment','utility_equipment','pipings']
            .map(v => `<option value="${v}">${v ? v : '-'}</option>`).join('');

        // 현재 카테고리 → 가상 키 변환 (instrument + tagDcs 조합)
        function pidVcat(item) {
            if (item.category === 'instrument') return item.tagDcs ? 'instrument_dcs' : 'instrument_field';
            return item.category || '';
        }

        tbody.innerHTML = data.items.map(item => {
            const vcat = pidVcat(item);
            const catOpts = CATEGORY_ORDER.map(k =>
                `<option value="${k}" ${vcat === k ? 'selected' : ''}>${CATEGORY_META[k]?.label || k}</option>`
            ).join('');
            return `<tr>
                <td>${item.id}</td>
                <td><input class="inp pid-edit" data-id="${item.id}" data-field="tagNo" value="${esc((item.tagName||'').toUpperCase())}" style="width:100%;font-family:var(--mono)"/></td>
                <td><input class="inp pid-edit" data-id="${item.id}" data-field="equipmentName" value="${esc(item.equipmentName) || ''}" style="width:100%"/></td>
                <td><input class="inp pid-edit" data-id="${item.id}" data-field="instrumentType" value="${esc((item.instrumentType||'').toUpperCase())}" style="width:100%"/></td>
                <td>
                    <select class="inp pid-edit-vcat" data-id="${item.id}" style="width:100%">
                        <option value="">-</option>
                        ${catOpts}
                    </select>
                </td>
                <td><input class="inp pid-edit" data-id="${item.id}" data-field="role" value="${esc(item.role) || ''}" style="width:100%"/></td>
                <td><input class="inp pid-edit" data-id="${item.id}" data-field="fromTag" value="${esc(item.fromTag) || ''}" style="width:100%"/></td>
                <td><input class="inp pid-edit" data-id="${item.id}" data-field="fromAt" value="${esc(item.fromAt) || ''}" style="width:100%"/></td>
                <td><input class="inp pid-edit" data-id="${item.id}" data-field="toTag" value="${esc(item.toTag) || ''}" style="width:100%"/></td>
                <td><input class="inp pid-edit" data-id="${item.id}" data-field="toAt" value="${esc(item.toAt) || ''}" style="width:100%"/></td>
                <td><input class="inp pid-edit" data-id="${item.id}" data-field="subArea" value="${esc(item.subArea) || ''}" placeholder="예: P9-1" style="width:100%;font-family:var(--mono)"/></td>
                <td>
                    ${item.experionTagId
                        ? `<button class="btn-sm btn-b" onclick="pidOpenMapping(${item.id}, this)">✅</button>`
                        : `<button class="btn-sm btn-b" onclick="pidOpenMapping(${item.id}, this)">매핑</button>`
                    }
                </td>
                <td style="text-align:center">
                    <button class="btn-sm btn-a" onclick="pidSaveRow(${item.id})">💾</button>
                </td>
                <td style="text-align:center">
                    <button class="btn-sm btn-b" onclick="pidDeleteRow(${item.id})" title="삭제">✕</button>
                </td>
            </tr>`;
        }).join('');
        
        pidRenderPagination(data.total, page);
    } catch (e) {
        tbody.innerHTML = `<tr><td colspan="14" style="text-align:center;padding:20px;color:var(--red)">오류: ${e.message}</td></tr>`;
    }
}

function pidRenderPagination(total, currentPage) {
    const pagination = document.getElementById('pid-pagination');
    if (!pagination) return;
    
    const totalPages = Math.ceil(total / pidPageSize);
    if (totalPages <= 1) {
        pagination.innerHTML = '';
        return;
    }
    
    let html = '';
    const start = Math.max(1, currentPage - 10);
    const end = Math.min(totalPages, currentPage + 10);
    
    if (currentPage > 1) {
        html += `<button class="btn-sm" onclick="pidLoadTable(${currentPage - 1})">‹</button>`;
    }
    
    for (let i = start; i <= end; i++) {
        html += `<button class="btn-sm ${i === currentPage ? 'btn-a' : 'btn-b'}"
                 onclick="pidLoadTable(${i})">${i}</button>`;
    }
    
    if (currentPage < totalPages) {
        html += `<button class="btn-sm" onclick="pidLoadTable(${currentPage + 1})">›</button>`;
    }
    
    pagination.innerHTML = html;
}

function pidShowAddRow() {
    const tbody = document.getElementById('pid-table-body');
    if (!tbody) return;
    // 이미 추가 행이 있으면 제거
    const existing = tbody.querySelector('tr.pid-add-row');
    if (existing) { existing.remove(); return; }

    const row = document.createElement('tr');
    row.className = 'pid-add-row';
    row.innerHTML = `
        <td style="color:var(--t2);font-size:11px">NEW</td>
        <td><input class="inp pid-add-input" data-field="tagNo" placeholder="예: FT-9999" style="width:100%;font-family:var(--mono)"/></td>
        <td><input class="inp pid-add-input" data-field="equipmentName" placeholder="장비명" style="width:100%"/></td>
        <td><input class="inp pid-add-input" data-field="instrumentType" placeholder="유형" style="width:100%"/></td>
        <td>
            <select class="inp pid-add-vcat" style="width:100%">
                <option value="">-</option>
                ${CATEGORY_ORDER.map(k =>
                    `<option value="${k}">${CATEGORY_META[k]?.label || k}</option>`
                ).join('')}
            </select>
        </td>
        <td><input class="inp pid-add-input" data-field="role" placeholder="Role" style="width:100%"/></td>
        <td><input class="inp pid-add-input" data-field="fromTag" placeholder="From" style="width:100%"/></td>
        <td><input class="inp pid-add-input" data-field="fromAt" placeholder="From_at" style="width:100%"/></td>
        <td><input class="inp pid-add-input" data-field="toTag" placeholder="To" style="width:100%"/></td>
        <td><input class="inp pid-add-input" data-field="toAt" placeholder="To_at" style="width:100%"/></td>
        <td><input class="inp pid-add-input" data-field="subArea" placeholder="예: P9-1" style="width:100%;font-family:var(--mono)"/></td>
        <td></td>
        <td style="text-align:center"><button class="btn-sm btn-a" onclick="pidCreateRow(this)">💾</button></td>
        <td style="text-align:center"><button class="btn-sm btn-b" onclick="this.closest('tr').remove()">✕</button></td>
    `;
    tbody.insertBefore(row, tbody.firstChild);
    row.querySelector('.pid-add-input[data-field="tagNo"]').focus();
}

async function pidCreateRow(btn) {
    const row = btn.closest('tr');
    const inputs = row.querySelectorAll('.pid-add-input');
    const vcatEl = row.querySelector('.pid-add-vcat');
    const vcat = vcatEl ? vcatEl.value : '';
    const { category, tagDcs } = pidResolveCat(vcat);
    const body = { tagNo: '', category: category || null, tagDcs };
    for (const inp of inputs) {
        body[inp.dataset.field] = inp.value || null;
    }
    if (!body.tagNo) { alert('태그번호를 입력하세요.'); return; }
    try {
        const res = await fetch('/api/pid', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(body)
        });
        if (!res.ok) {
            const err = await res.json().catch(() => ({ error: res.statusText }));
            throw new Error(err.error || res.statusText);
        }
        row.remove();
        await pidLoadTable(pidCurrentPage);
        pidUpdateStats();
    } catch (e) {
        alert(`추가 실패: ${e.message}`);
    }
}

async function pidSaveRow(id) {
    const inputs = document.querySelectorAll(`.pid-edit[data-id="${id}"]`);
    const vcatEl = document.querySelector(`.pid-edit-vcat[data-id="${id}"]`);
    const vcat = vcatEl ? vcatEl.value : '';
    const { category, tagDcs } = pidResolveCat(vcat);
    const body = { category: category || null, tagDcs };
    for (const inp of inputs) {
        // subArea: 빈 문자열도 전송 (= tag_metadata에서 삭제)
        body[inp.dataset.field] = inp.dataset.field === 'subArea' ? inp.value : (inp.value || null);
    }
    try {
        const res = await fetch(`/api/pid/${id}`, {
            method: 'PUT',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(body)
        });
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
    } catch (e) {
        alert(`저장 실패: ${e.message}`);
    }
}

async function pidDeleteRow(id) {
    if (!confirm('정말 이 레코드를 삭제하시겠습니까?')) return;

    try {
        const res = await fetch(`/api/pid/${id}`, { method: 'DELETE' });
        if (!res.ok) {
            const err = await res.json().catch(() => ({}));
            throw new Error(err.error || res.statusText);
        }
        await pidLoadTable(pidCurrentPage);
        pidUpdateStats();
    } catch (e) {
        alert(`삭제 실패: ${e.message}`);
    }
}

function pidUpdateStats() {
    if (!pidLastResult || !pidLastResult.items) return;
    
    const total = pidLastResult.total || 0;
    const highConf = pidLastResult.items.filter(i => i.confidence >= 0.7).length;
    const mapped = pidLastResult.items.filter(i => i.experionTagId).length;
    
    const elTotal = document.getElementById('pid-stat-total');
    const elHigh = document.getElementById('pid-stat-high');
    const elMapped = document.getElementById('pid-stat-mapped');
    
    if (elTotal) elTotal.textContent = total;
    if (elHigh) elHigh.textContent = highConf;
    if (elMapped) elMapped.textContent = mapped;
}

function pidClearLog() {
    const logEl = document.getElementById('pid-log');
    if (logEl) logEl.innerHTML = '';
}

async function pidAnalyzeConnections() {
    const fileName = document.getElementById('pid-server-file').value;
    const statusEl = document.getElementById('pid-status');
    const logEl = document.getElementById('pid-log');

    if (!fileName) {
        if (statusEl) statusEl.textContent = '❌ 서버 파일을 선택하세요.';
        return;
    }

    if (statusEl) statusEl.textContent = '연결 분석 중...';
    if (logEl) {
        logEl.style.display = 'block';
        logEl.innerHTML = '<div class="ll inf">⏳ 연결 분석 시작...</div>';
    }

    try {
        const res = await fetch('/api/pid/connections', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ fileName })
        });
        if (!res.ok) {
            const err = await res.json().catch(() => ({ error: res.statusText }));
            throw new Error(err.error || res.statusText);
        }
        const data = await res.json();
        if (statusEl) statusEl.textContent = `✅ 연결 분석 완료: ${data.connectionCount}건`;
        log('pid-log', [
            { c: 'ok', t: `✅ 연결 분석 완료: ${data.connectionCount}개 from→to 연결` }
        ]);
        pidCurrentPage = 1;
        await pidLoadTable();
        pidUpdateStats();
    } catch (e) {
        if (statusEl) statusEl.textContent = `❌ 오류: ${e.message}`;
        log('pid-log', [{ c: 'err', t: `❌ ${e.message}` }]);
    }
}

async function pidDeleteAll() {
    if (!confirm('P&ID 추출 데이터를 전부 삭제하시겠습니까?\n이 작업은 되돌릴 수 없습니다.')) return;

    const statusEl = document.getElementById('pid-status');
    const logEl = document.getElementById('pid-log');

    if (statusEl) statusEl.textContent = '전체 삭제 중...';
    if (logEl) {
        logEl.style.display = 'block';
        logEl.innerHTML = '<div class="ll inf">⏳ 삭제 중...</div>';
    }

    try {
        const res = await fetch('/api/pid/all', { method: 'DELETE' });
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        const data = await res.json();
        if (statusEl) statusEl.textContent = `🗑 ${data.message}`;
        log('pid-log', [{ c: 'warn', t: `🗑 전체 삭제 완료: ${data.deletedCount}건` }]);
        pidCurrentPage = 1;
        await pidLoadTable();
        pidUpdateStats();
    } catch (e) {
        if (statusEl) statusEl.textContent = `❌ 오류: ${e.message}`;
        log('pid-log', [{ c: 'err', t: `❌ ${e.message}` }]);
    }
}

/* ── Prefix 분류 정의 ─────────────────────────────────────── */
let pidPrefixPanelVisible = false;

function pidTogglePrefixPanel() {
    const panel = document.getElementById('pid-prefix-panel');
    const toggle = document.getElementById('pid-prefix-toggle');
    pidPrefixPanelVisible = !pidPrefixPanelVisible;
    if (panel) panel.style.display = pidPrefixPanelVisible ? 'block' : 'none';
    if (toggle) toggle.textContent = pidPrefixPanelVisible ? '▲' : '▼';
    if (pidPrefixPanelVisible) pidRefreshPrefixRules();
}

const CATEGORY_META = {
    // ── instrument 가상 분할 (DB category='instrument', tag_dcs로 구분) ──
    instrument_dcs:   { label: 'DCS 태그',         badge: 'warn', dbCat: 'instrument', tagDcs: true  },
    instrument_field: { label: '현장 계기',          badge: 'ok',   dbCat: 'instrument', tagDcs: false },
    // ── 그 외 equipment / pipings ──
    power_equipment:  { label: 'Power Equipment',   badge: 'warn', dbCat: 'power_equipment',  tagDcs: false },
    storage_equipment:{ label: 'Storage Equipment', badge: 'inf',  dbCat: 'storage_equipment', tagDcs: false },
    process_equipment:{ label: 'Process Equipment', badge: '',     dbCat: 'process_equipment', tagDcs: false },
    utility_equipment:{ label: 'Utility Equipment', badge: 'warn', dbCat: 'utility_equipment', tagDcs: false },
    pipings:          { label: 'Pipings',            badge: 'ok',   dbCat: 'pipings',           tagDcs: false },
    // ── 장비 테이블 배지용 (equipment 목록의 r.category 값과 매핑) ──
    instrument:       { label: 'Instrument', badge: 'ok' },
};
const CATEGORY_ORDER = [
    'instrument_dcs', 'instrument_field',
    'power_equipment', 'storage_equipment', 'process_equipment', 'utility_equipment', 'pipings'
];

function pidCategoryBadge(cat) {
    return CATEGORY_META[cat] ? CATEGORY_META[cat].badge : '';
}

async function pidRefreshPrefixRules() {
    const container = document.getElementById('pid-prefix-groups');
    if (!container) return;
    container.innerHTML = '<div style="text-align:center;padding:12px;color:var(--t2)">로딩 중...</div>';

    try {
        const res = await fetch('/api/pid/prefix-rules');
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        const data = await res.json();
        const items = data.items || [];

        if (items.length === 0) {
            container.innerHTML = '<div style="text-align:center;padding:12px;color:var(--t2)">규칙이 없습니다. 아래에서 추가하세요.</div>';
            return;
        }

        // instrument → tag_dcs 기준으로 가상 분할
        const grouped = {};
        for (const r of items) {
            let key = r.category;
            if (r.category === 'instrument') {
                key = r.tagDcs ? 'instrument_dcs' : 'instrument_field';
            }
            if (!grouped[key]) grouped[key] = [];
            grouped[key].push(r);
        }

        let html = '';
        for (const vcat of CATEGORY_ORDER) {
            const rules = grouped[vcat];
            const meta = CATEGORY_META[vcat] || { label: vcat, badge: '', dbCat: vcat, tagDcs: false };
            rules?.sort((a, b) => a.sortOrder - b.sortOrder);

            html += `<div class="pid-cat-group" data-vcat="${vcat}">
                <div class="pid-cat-header">
                    <span class="badge ${meta.badge}">${meta.label}</span>
                    <span class="pid-cat-add" style="margin-left:auto">
                        <input class="inp pid-cat-prefix-input" placeholder="예: ${vcat === 'instrument_dcs' ? 'FIC' : vcat === 'instrument_field' ? 'FT' : 'P-'}" style="width:90px;font-family:var(--mono)" />
                        <input class="inp pid-cat-desc-input" placeholder="설명 (선택)" style="width:160px" />
                        <input class="inp pid-cat-order-input" type="number" value="10" style="width:50px" />
                        <button class="btn-a btn-sm" onclick="pidAddPrefixRule('${vcat}')">추가</button>
                    </span>
                </div>
                <div class="pid-cat-body">`;

            if (rules) {
                for (const r of rules) {
                    html += `<div class="pid-cat-row" data-sort-order="${r.sortOrder}">
                        <input class="inp pid-cat-prefix-input" value="${esc(r.prefix)}" style="width:80px;font-family:var(--mono)" />
                        <input class="inp pid-cat-desc-input" value="${esc(r.description) || ''}" style="flex:1;min-width:0" />
                        <span class="pid-cat-actions">
                            <button class="btn-sm btn-a" onclick="pidUpdatePrefixRule(${r.id}, this)">수정</button>
                            <button class="btn-sm btn-b" onclick="pidDeletePrefixRule(${r.id}, '${esc(r.prefix)}')">삭제</button>
                        </span>
                    </div>`;
                }
            }

            html += `</div></div>`;
        }

        container.innerHTML = html;
    } catch (e) {
        container.innerHTML = `<div style="text-align:center;padding:12px;color:var(--red)">오류: ${e.message}</div>`;
    }
}

// 가상 카테고리 키 → { DB category, tagDcs } 변환
function pidResolveCat(vcat) {
    const meta = CATEGORY_META[vcat];
    return {
        category: meta?.dbCat ?? vcat,
        tagDcs:   meta?.tagDcs ?? false
    };
}

async function pidAddPrefixRule(vcat) {
    const row = event.target.closest('.pid-cat-add');
    const prefix = row.querySelector('.pid-cat-prefix-input').value.trim();
    const desc   = row.querySelector('.pid-cat-desc-input').value.trim();
    const order  = parseInt(row.querySelector('.pid-cat-order-input').value) || 10;
    const { category, tagDcs } = pidResolveCat(vcat);   // 그룹이 tagDcs 결정

    if (!prefix) { alert('Prefix를 입력하세요.'); return; }

    try {
        const res = await fetch('/api/pid/prefix-rules', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ prefix, category, tagDcs, description: desc, sortOrder: order })
        });
        if (!res.ok) {
            const err = await res.json().catch(() => ({ error: res.statusText }));
            throw new Error(err.error || res.statusText);
        }
        row.querySelector('.pid-cat-prefix-input').value = '';
        row.querySelector('.pid-cat-desc-input').value = '';
        await pidRefreshPrefixRules();
    } catch (e) {
        alert(`추가 실패: ${e.message}`);
    }
}

async function pidUpdatePrefixRule(id, btn) {
    const row   = btn.closest('.pid-cat-row');
    const group = btn.closest('.pid-cat-group');
    const vcat  = group.dataset.vcat;                           // data-vcat 속성으로 가상 키 읽기
    const { category } = pidResolveCat(vcat);
    const prefix = row.querySelector('.pid-cat-prefix-input').value.trim();
    const desc   = row.querySelector('.pid-cat-desc-input').value.trim();
    const order  = parseInt(row.dataset.sortOrder) || 10;       // data-sort-order 속성에서 읽기
    // tagDcs는 그룹(vcat)이 결정 — 행에 별도 체크박스 없음
    // 그룹 이동이 필요하면 삭제 후 반대 그룹에서 추가
    const tagDcs = CATEGORY_META[vcat]?.tagDcs ?? false;

    if (!prefix) { alert('Prefix를 입력하세요.'); return; }

    try {
        const res = await fetch(`/api/pid/prefix-rules/${id}`, {
            method: 'PUT',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ prefix, category, tagDcs, description: desc, sortOrder: order })
        });
        if (!res.ok) {
            const err = await res.json().catch(() => ({ error: res.statusText }));
            throw new Error(err.error || res.statusText);
        }
        await pidRefreshPrefixRules();
    } catch (e) {
        alert(`수정 실패: ${e.message}`);
    }
}

async function pidDeletePrefixRule(id, prefix) {
    if (!confirm(`"${prefix}" 규칙을 삭제하시겠습니까?`)) return;
    try {
        const res = await fetch(`/api/pid/prefix-rules/${id}`, { method: 'DELETE' });
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        await pidRefreshPrefixRules();
    } catch (e) {
        alert(`삭제 실패: ${e.message}`);
    }
}

async function pidApplyCategories() {
    if (!confirm('전체 레코드에 현재 PREFIX 분류 정의를 재적용하시겠습니까?\n수동으로 변경한 카테고리도 덮어쓰여집니다.')) return;
    try {
        const res = await fetch('/api/pid/apply-categories', { method: 'POST' });
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        const data = await res.json();
        alert(`${data.applied}건 category 적용 완료`);
        await pidLoadTable(pidCurrentPage);
        pidUpdateStats();
    } catch (e) {
        alert(`재적용 실패: ${e.message}`);
    }
}

async function pidOpenMapping(id, btn) {
    btn.disabled = true;
    btn.textContent = '⏳';
    try {
        const res = await fetch(`/api/pid/${id}/auto-map`, { method: 'POST' });
        if (!res.ok) {
            btn.textContent = '❌';
            btn.title = '매칭 실패';
            return;
        }
        const data = await res.json();
        btn.textContent = '✅';
    } catch {
        btn.textContent = '❌';
        btn.title = '오류';
    }
}

// 탭 진입 시 초기화
paneInit.pid = async function() {
    pidCurrentPage = 1;
    pidLastResult = null;
    if (document.getElementById('pid-file-input')) document.getElementById('pid-file-input').value = '';
    if (!pidExtracting) {
        const st = document.getElementById('pid-status');
        if (st) st.textContent = '대기 중...';
        const elapsedEl = document.getElementById('pid-elapsed');
        if (elapsedEl) elapsedEl.style.display = 'none';
        if (pidElapsedInterval) {
            clearInterval(pidElapsedInterval);
            pidElapsedInterval = null;
        }
        const tb = document.getElementById('pid-table-body');
        if (tb) tb.innerHTML = '';
        const pg = document.getElementById('pid-pagination');
        if (pg) pg.innerHTML = '';
        const stTot = document.getElementById('pid-stat-total');
        if (stTot) stTot.textContent = '0';
        const stHi = document.getElementById('pid-stat-high');
        if (stHi) stHi.textContent = '0';
        const stMap = document.getElementById('pid-stat-mapped');
        if (stMap) stMap.textContent = '0';
        await pidLoadTable(1);
    }

    // Export buttons — attach once
    const bind = (id, fn) => {
        const el = document.getElementById(id);
        if (el && !el.dataset.pidBound) { el.addEventListener('click', fn); el.dataset.pidBound = '1'; }
    };
    bind('btn-pid-export-csv', async () => {
        if (!pidLastResult) return;
        const res = await fetch('/api/pid/export/csv');
        if (!res.ok) return;
        const blob = await res.blob();
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `pid-export-${new Date().toISOString().slice(0,10)}.csv`;
        a.click();
        URL.revokeObjectURL(url);
    });
    bind('btn-pid-export-excel', async () => {
        if (!pidLastResult) return;
        const res = await fetch('/api/pid/export/excel');
        if (!res.ok) return;
        const blob = await res.blob();
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `pid-export-${new Date().toISOString().slice(0,10)}.xlsx`;
        a.click();
        URL.revokeObjectURL(url);
    });
};
