/* schedule.js — Gantt chart with row virtualization */

let ganttData = { entries: [], dependencies: [], milestones: [] };
let projects = [];
let phases = [];
let templates = [];
let allRows = []; // flat row data for virtualization
let collapseState = {}; // { projectCode: bool }
let ncCache = {}; // { projectId: [issues] }

const ROW_HEIGHT = 32;
const BUFFER_ROWS = 5;
const DAY_WIDTHS = { month: 24, quarter: 12, year: 3 };
let currentZoom = 'quarter';
let currentYear = new Date().getFullYear();
let canEdit = false;

/* ===== Init ===== */
document.addEventListener('DOMContentLoaded', async () => {
    const ok = await initAuth();
    if (!ok) return;
    document.querySelector('.fade-in').classList.add('visible');

    // Check edit permission (support_team+)
    const role = currentUser?.role || '';
    canEdit = ['support_team', 'admin', 'system', 'system admin'].includes(role);
    if (canEdit) {
        document.getElementById('btnAddEntry').classList.remove('hidden');
        document.getElementById('btnBatchAdd').classList.remove('hidden');
        document.getElementById('btnAddMilestone').classList.remove('hidden');
        const btnGen = document.getElementById('btnGenTemplate');
        if (btnGen) btnGen.classList.remove('hidden');
    }

    // Load collapse state
    try {
        const saved = localStorage.getItem('gantt_collapse');
        if (saved) collapseState = JSON.parse(saved);
    } catch {}

    // Year select
    const sel = document.getElementById('yearSelect');
    for (let y = currentYear - 2; y <= currentYear + 2; y++) {
        const opt = document.createElement('option');
        opt.value = y; opt.textContent = y;
        if (y === currentYear) opt.selected = true;
        sel.appendChild(opt);
    }
    sel.addEventListener('change', () => { currentYear = parseInt(sel.value); loadGantt(); });

    // Zoom buttons
    document.querySelectorAll('.zoom-btn').forEach(btn => {
        btn.addEventListener('click', () => {
            document.querySelectorAll('.zoom-btn').forEach(b => b.classList.remove('active'));
            btn.classList.add('active');
            currentZoom = btn.dataset.zoom;
            renderGantt();
        });
    });

    // Toolbar buttons
    document.getElementById('btnAddEntry').addEventListener('click', () => openEntryModal());
    document.getElementById('btnBatchAdd').addEventListener('click', () => openBatchModal());
    document.getElementById('btnAddMilestone').addEventListener('click', () => openMilestoneModal());

    await loadMasterData();
    await loadGantt();
});

async function loadMasterData() {
    try {
        const [projRes, phaseRes, tmplRes] = await Promise.all([
            api('/projects'), api('/schedule/phases'), api('/schedule/templates')
        ]);
        projects = projRes.data || [];
        phases = phaseRes.data || [];
        templates = tmplRes.data || [];
    } catch (err) { showToast('마스터 데이터 로드 실패', 'error'); }
}

async function loadGantt() {
    try {
        const res = await api(`/schedule/entries/gantt?year=${currentYear}`);
        ganttData = res.data;
        // Preload NC data
        const projectIds = [...new Set(ganttData.entries.map(e => e.project_id))];
        await Promise.all(projectIds.map(async pid => {
            try {
                const ncRes = await api(`/schedule/nonconformance?project_id=${pid}`);
                ncCache[pid] = ncRes.data || [];
            } catch { ncCache[pid] = []; }
        }));
        renderGantt();
    } catch (err) { showToast('공정표 데이터 로드 실패: ' + err.message, 'error'); }
}

/* ===== Build flat row array ===== */
function buildRows() {
    allRows = [];
    // Group entries by project, then by phase
    const byProject = {};
    ganttData.entries.forEach(e => {
        if (!byProject[e.project_code]) byProject[e.project_code] = { project_id: e.project_id, project_name: e.project_name, code: e.project_code, phases: {} };
        const p = byProject[e.project_code];
        if (!p.phases[e.phase_name]) p.phases[e.phase_name] = { phase_id: e.phase_id, color: e.phase_color, order: e.phase_order, entries: [] };
        p.phases[e.phase_name].entries.push(e);
    });
    // Also add milestones-only projects
    ganttData.milestones.forEach(m => {
        if (!byProject[m.project_code]) byProject[m.project_code] = { project_id: m.project_id, project_name: m.project_name, code: m.project_code, phases: {} };
    });

    const sortedProjects = Object.values(byProject).sort((a, b) => a.code.localeCompare(b.code));

    for (const proj of sortedProjects) {
        const collapsed = collapseState[proj.code] === true;
        allRows.push({ type: 'project', code: proj.code, label: `${proj.code} ${proj.project_name}`, project_id: proj.project_id, collapsed });

        if (!collapsed) {
            const sortedPhases = Object.entries(proj.phases).sort((a, b) => a[1].order - b[1].order);
            for (const [phaseName, phaseData] of sortedPhases) {
                allRows.push({ type: 'phase', label: phaseName, color: phaseData.color });
                for (const entry of phaseData.entries) {
                    allRows.push({ type: 'task', entry, color: phaseData.color });
                }
            }
            // Milestones for this project
            const projMilestones = ganttData.milestones.filter(m => m.project_id === proj.project_id);
            if (projMilestones.length > 0) {
                allRows.push({ type: 'milestone-header', label: '◆ 마일스톤', milestones: projMilestones });
            }
            // NC row
            const ncList = ncCache[proj.project_id] || [];
            if (ncList.length > 0) {
                allRows.push({ type: 'nc', label: `⚠ 부적합 (${ncList.length})`, project_id: proj.project_id, count: ncList.length });
            }
        }
    }
}

/* ===== Render ===== */
function renderGantt() {
    buildRows();
    const container = document.getElementById('ganttContainer');
    const wrapper = document.getElementById('ganttWrapper');
    const dayWidth = DAY_WIDTHS[currentZoom];

    // Calculate total days in year
    const yearStart = new Date(currentYear, 0, 1);
    const yearEnd = new Date(currentYear, 11, 31);
    const totalDays = Math.ceil((yearEnd - yearStart) / 86400000) + 1;
    const timelineWidth = totalDays * dayWidth;

    container.style.setProperty('--day-width', dayWidth + 'px');
    container.style.width = (250 + timelineWidth) + 'px';

    // Build month header
    let headerHtml = '<div class="gantt-month-header"><div class="gantt-label"><div class="label-content font-semibold text-sm text-gray-600">프로젝트 / 단계 / 작업</div></div><div class="gantt-timeline">';
    for (let m = 0; m < 12; m++) {
        const daysInMonth = new Date(currentYear, m + 1, 0).getDate();
        const monthWidth = daysInMonth * dayWidth;
        const monthNames = ['1월','2월','3월','4월','5월','6월','7월','8월','9월','10월','11월','12월'];
        headerHtml += `<div class="gantt-day month-label" style="flex: 0 0 ${monthWidth}px;">${monthNames[m]}</div>`;
    }
    headerHtml += '</div></div>';

    // Virtual scroll container
    const totalHeight = allRows.length * ROW_HEIGHT;
    let rowsHtml = `<div style="height:${totalHeight}px;position:relative;" id="ganttVirtualBody"></div>`;

    container.innerHTML = headerHtml + rowsHtml;

    // Today marker
    const today = new Date();
    if (today.getFullYear() === currentYear) {
        const todayOffset = dayOfYear(today) - 1;
        const markerLeft = 250 + todayOffset * dayWidth;
        const marker = document.createElement('div');
        marker.className = 'today-marker';
        marker.style.left = markerLeft + 'px';
        container.appendChild(marker);
    }

    // Setup virtual scroll
    const virtualBody = document.getElementById('ganttVirtualBody');
    const onScroll = () => renderVisibleRows(wrapper, virtualBody, dayWidth, totalDays);
    wrapper.addEventListener('scroll', onScroll);
    renderVisibleRows(wrapper, virtualBody, dayWidth, totalDays);

    // Scroll to today
    if (today.getFullYear() === currentYear) {
        const todayOffset = dayOfYear(today) - 1;
        const scrollTo = Math.max(0, todayOffset * dayWidth - wrapper.clientWidth / 2 + 250);
        wrapper.scrollLeft = scrollTo;
    }
}

function renderVisibleRows(wrapper, virtualBody, dayWidth, totalDays) {
    const scrollTop = wrapper.scrollTop - 30; // account for header
    const viewHeight = wrapper.clientHeight;
    const startIdx = Math.max(0, Math.floor(scrollTop / ROW_HEIGHT) - BUFFER_ROWS);
    const endIdx = Math.min(allRows.length, Math.ceil((scrollTop + viewHeight) / ROW_HEIGHT) + BUFFER_ROWS);

    let html = '';
    for (let i = startIdx; i < endIdx; i++) {
        const row = allRows[i];
        const top = i * ROW_HEIGHT;
        html += renderRow(row, top, dayWidth, totalDays);
    }
    virtualBody.innerHTML = html;
}

function renderRow(row, top, dayWidth, totalDays) {
    const style = `position:absolute;top:${top}px;width:100%;height:${ROW_HEIGHT}px;`;

    if (row.type === 'project') {
        const arrowClass = row.collapsed ? 'collapsed' : '';
        return `<div class="gantt-row project-row" style="${style}">
            <div class="gantt-label"><div class="label-content collapse-toggle ${arrowClass}" onclick="toggleProject('${row.code}')">
                <span class="arrow">▼</span>${escapeHtml(row.label)}
            </div></div>
            <div class="gantt-timeline" style="width:${totalDays * dayWidth}px;position:relative;"></div>
        </div>`;
    }

    if (row.type === 'phase') {
        return `<div class="gantt-row phase-row" style="${style}">
            <div class="gantt-label"><div class="label-content"><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:${row.color};margin-right:6px;"></span>${escapeHtml(row.label)}</div></div>
            <div class="gantt-timeline" style="width:${totalDays * dayWidth}px;"></div>
        </div>`;
    }

    if (row.type === 'task') {
        const e = row.entry;
        const bar = calcBar(e.start_date, e.end_date, dayWidth);
        const statusColors = { planned: '0.6', in_progress: '0.85', completed: '1', delayed: '0.9' };
        const opacity = statusColors[e.status] || '0.7';
        const barHtml = bar ? `<div class="gantt-bar" style="left:${bar.left}px;width:${bar.width}px;background:${row.color};opacity:${opacity};"
            onclick="showBarDetail(${e.entry_id})" title="${escapeHtml(e.task_name)}&#10;${formatDate(e.start_date)}~${formatDate(e.end_date)}&#10;진행률: ${e.progress}%">
            <div class="gantt-bar-progress" style="width:${e.progress}%;background:#fff;"></div>
            ${bar.width > 50 ? `<div class="gantt-bar-label">${escapeHtml(e.task_name)}</div>` : ''}
        </div>` : '';

        return `<div class="gantt-row task-row" style="${style}">
            <div class="gantt-label"><div class="label-content" title="${escapeHtml(e.task_name)}">${escapeHtml(e.task_name)}${e.assignee ? ` <span class="text-gray-400 text-xs">(${escapeHtml(e.assignee)})</span>` : ''}</div></div>
            <div class="gantt-timeline" style="width:${totalDays * dayWidth}px;position:relative;">${barHtml}</div>
        </div>`;
    }

    if (row.type === 'milestone-header') {
        let markers = '';
        for (const m of row.milestones) {
            const pos = calcPos(m.milestone_date, dayWidth);
            if (pos !== null) {
                const mColor = m.status === 'completed' ? '#10B981' : m.status === 'missed' ? '#EF4444' : '#7C3AED';
                markers += `<div class="milestone-marker" style="left:${pos - 7}px;background:${mColor};" title="${escapeHtml(m.milestone_name)}&#10;${formatDate(m.milestone_date)}" onclick="showMilestoneDetail(${m.milestone_id})"></div>`;
            }
        }
        return `<div class="gantt-row milestone-row" style="${style}">
            <div class="gantt-label"><div class="label-content">${escapeHtml(row.label)}</div></div>
            <div class="gantt-timeline" style="width:${totalDays * dayWidth}px;position:relative;">${markers}</div>
        </div>`;
    }

    if (row.type === 'nc') {
        // Place NC badges by date
        const ncList = ncCache[row.project_id] || [];
        let badges = '';
        const byMonth = {};
        ncList.forEach(nc => {
            const d = nc.report_date ? new Date(nc.report_date) : null;
            if (d && d.getFullYear() === currentYear) {
                const m = d.getMonth();
                byMonth[m] = (byMonth[m] || 0) + 1;
            }
        });
        for (const [m, cnt] of Object.entries(byMonth)) {
            const monthStart = new Date(currentYear, parseInt(m), 15);
            const pos = calcPos(monthStart, dayWidth);
            if (pos !== null) {
                badges += `<div class="nc-badge" style="left:${pos - 10}px;" onclick="showNcPopup(${row.project_id})">${cnt}</div>`;
            }
        }
        return `<div class="gantt-row nc-row" style="${style}">
            <div class="gantt-label"><div class="label-content" style="cursor:pointer;" onclick="showNcPopup(${row.project_id})">${escapeHtml(row.label)}</div></div>
            <div class="gantt-timeline" style="width:${totalDays * dayWidth}px;position:relative;">${badges}</div>
        </div>`;
    }

    return '';
}

/* ===== Helpers ===== */
function dayOfYear(d) {
    const start = new Date(d.getFullYear(), 0, 1);
    return Math.ceil((d - start) / 86400000) + 1;
}

function calcBar(startStr, endStr, dayWidth) {
    const s = new Date(startStr);
    const e = new Date(endStr);
    if (s.getFullYear() > currentYear || e.getFullYear() < currentYear) return null;
    const yearStart = new Date(currentYear, 0, 1);
    const yearEnd = new Date(currentYear, 11, 31);
    const clampStart = s < yearStart ? yearStart : s;
    const clampEnd = e > yearEnd ? yearEnd : e;
    const startDay = Math.ceil((clampStart - yearStart) / 86400000);
    const endDay = Math.ceil((clampEnd - yearStart) / 86400000) + 1;
    return { left: startDay * dayWidth, width: Math.max((endDay - startDay) * dayWidth, 4) };
}

function calcPos(dateStr, dayWidth) {
    const d = new Date(dateStr);
    if (d.getFullYear() !== currentYear) return null;
    const yearStart = new Date(currentYear, 0, 1);
    const offset = Math.ceil((d - yearStart) / 86400000);
    return offset * dayWidth;
}

/* ===== Interactions ===== */
function toggleProject(code) {
    collapseState[code] = !collapseState[code];
    localStorage.setItem('gantt_collapse', JSON.stringify(collapseState));
    renderGantt();
}

function showBarDetail(entryId) {
    const entry = ganttData.entries.find(e => e.entry_id === entryId);
    if (!entry) return;
    const popup = document.getElementById('barDetailPopup');
    document.getElementById('barDetailTitle').textContent = entry.task_name;
    const statusLabels = { planned: '계획', in_progress: '진행중', completed: '완료', delayed: '지연', cancelled: '취소' };
    document.getElementById('barDetailContent').innerHTML = `
        <div class="space-y-2 text-sm">
            <div class="flex justify-between"><span class="text-gray-500">프로젝트</span><span>${escapeHtml(entry.project_code)} ${escapeHtml(entry.project_name)}</span></div>
            <div class="flex justify-between"><span class="text-gray-500">공정 단계</span><span>${escapeHtml(entry.phase_name)}</span></div>
            <div class="flex justify-between"><span class="text-gray-500">기간</span><span>${formatDate(entry.start_date)} ~ ${formatDate(entry.end_date)}</span></div>
            <div class="flex justify-between"><span class="text-gray-500">진행률</span><span>${entry.progress}%</span></div>
            <div class="flex justify-between"><span class="text-gray-500">상태</span><span>${statusLabels[entry.status] || entry.status}</span></div>
            ${entry.assignee ? `<div class="flex justify-between"><span class="text-gray-500">담당자</span><span>${escapeHtml(entry.assignee)}</span></div>` : ''}
            ${entry.notes ? `<div><span class="text-gray-500">메모:</span> ${escapeHtml(entry.notes)}</div>` : ''}
        </div>
    `;
    let actions = '';
    if (canEdit) {
        actions = `<button onclick="document.getElementById('barDetailPopup').classList.add('hidden');openEntryModal(${entryId})" class="px-3 py-1.5 text-sm bg-orange-600 text-white rounded-lg hover:bg-orange-700">수정</button>`;
    }
    document.getElementById('barDetailActions').innerHTML = actions;
    popup.classList.remove('hidden');
}

function showMilestoneDetail(milestoneId) {
    const m = ganttData.milestones.find(ms => ms.milestone_id === milestoneId);
    if (!m) return;
    const popup = document.getElementById('barDetailPopup');
    const typeLabels = { deadline: '납기', review: '검토', inspection: '검사', delivery: '출하', meeting: '회의', other: '기타' };
    const statusLabels = { upcoming: '예정', completed: '완료', missed: '미달성' };
    document.getElementById('barDetailTitle').textContent = '◆ ' + m.milestone_name;
    document.getElementById('barDetailContent').innerHTML = `
        <div class="space-y-2 text-sm">
            <div class="flex justify-between"><span class="text-gray-500">프로젝트</span><span>${escapeHtml(m.project_code)} ${escapeHtml(m.project_name)}</span></div>
            <div class="flex justify-between"><span class="text-gray-500">날짜</span><span>${formatDate(m.milestone_date)}</span></div>
            <div class="flex justify-between"><span class="text-gray-500">유형</span><span>${typeLabels[m.milestone_type] || m.milestone_type}</span></div>
            <div class="flex justify-between"><span class="text-gray-500">상태</span><span>${statusLabels[m.status] || m.status}</span></div>
            ${m.notes ? `<div><span class="text-gray-500">메모:</span> ${escapeHtml(m.notes)}</div>` : ''}
        </div>
    `;
    let actions = '';
    if (canEdit) {
        actions = `<button onclick="document.getElementById('barDetailPopup').classList.add('hidden');openMilestoneModal(${milestoneId})" class="px-3 py-1.5 text-sm bg-purple-600 text-white rounded-lg hover:bg-purple-700">수정</button>`;
    }
    document.getElementById('barDetailActions').innerHTML = actions;
    popup.classList.remove('hidden');
}

function showNcPopup(projectId) {
    const list = ncCache[projectId] || [];
    const proj = projects.find(p => p.project_id === projectId);
    document.getElementById('ncPopupTitle').textContent = `부적합 현황 - ${proj ? proj.job_no : ''}`;
    const statusLabels = { reported: '신고', received: '접수', reviewing: '검토중', in_progress: '처리중', completed: '완료' };
    let html = '';
    if (list.length === 0) {
        html = '<p class="text-gray-500 text-sm">부적합 내역이 없습니다.</p>';
    } else {
        html = '<table class="data-table"><thead><tr><th>일자</th><th>분류</th><th>내용</th><th>상태</th></tr></thead><tbody>';
        for (const nc of list) {
            html += `<tr>
                <td>${formatDate(nc.report_date)}</td>
                <td>${escapeHtml(nc.category || '-')}</td>
                <td class="max-w-[200px] truncate">${escapeHtml(nc.description || '-')}</td>
                <td><span class="badge ${nc.review_status === 'completed' ? 'badge-green' : 'badge-amber'}">${statusLabels[nc.review_status] || nc.review_status}</span></td>
            </tr>`;
        }
        html += '</tbody></table>';
    }
    document.getElementById('ncPopupContent').innerHTML = html;
    document.getElementById('ncPopup').classList.remove('hidden');
}

/* ===== Entry Modal ===== */
function openEntryModal(editId) {
    const modal = document.getElementById('entryModal');
    const isEdit = !!editId;
    document.getElementById('entryModalTitle').textContent = isEdit ? '공정표 항목 수정' : '공정표 항목 추가';

    // Populate dropdowns
    populateSelect('entryProject', projects, 'project_id', p => `${p.job_no} ${p.project_name}`);
    populateSelect('entryPhase', phases, 'phase_id', p => p.phase_name);

    // Template select (populated on phase change)
    const phaseSelect = document.getElementById('entryPhase');
    phaseSelect.addEventListener('change', () => loadTemplateOptions('entryTemplate', phaseSelect.value));
    if (phases.length > 0) loadTemplateOptions('entryTemplate', phaseSelect.value);

    // Template → task name
    document.getElementById('entryTemplate').addEventListener('change', function() {
        if (this.value) {
            const tmpl = templates.find(t => t.template_id === parseInt(this.value));
            if (tmpl) {
                document.getElementById('entryTaskName').value = tmpl.task_name;
                // Auto-fill duration
                const startDate = document.getElementById('entryStartDate').value;
                if (startDate && tmpl.default_duration_days) {
                    const end = new Date(startDate);
                    end.setDate(end.getDate() + tmpl.default_duration_days);
                    document.getElementById('entryEndDate').value = end.toISOString().split('T')[0];
                }
            }
        }
    });

    // Dependencies (all entries for the selected project)
    const depSelect = document.getElementById('entryDependencies');
    depSelect.innerHTML = '';

    if (isEdit) {
        const entry = ganttData.entries.find(e => e.entry_id === editId);
        if (!entry) return;
        document.getElementById('entryId').value = editId;
        document.getElementById('entryProject').value = entry.project_id;
        document.getElementById('entryPhase').value = entry.phase_id;
        document.getElementById('entryTaskName').value = entry.task_name;
        document.getElementById('entryStartDate').value = formatDate(entry.start_date);
        document.getElementById('entryEndDate').value = formatDate(entry.end_date);
        document.getElementById('entryAssignee').value = entry.assignee || '';
        document.getElementById('entryProgress').value = entry.progress;
        document.getElementById('entryStatus').value = entry.status;
        document.getElementById('entryNotes').value = entry.notes || '';

        // Load dependencies
        const projEntries = ganttData.entries.filter(e => e.project_id === entry.project_id && e.entry_id !== editId);
        const deps = ganttData.dependencies.filter(d => d.entry_id === editId).map(d => d.depends_on_entry_id);
        projEntries.forEach(e => {
            const opt = document.createElement('option');
            opt.value = e.entry_id;
            opt.textContent = `${e.phase_name} > ${e.task_name}`;
            opt.selected = deps.includes(e.entry_id);
            depSelect.appendChild(opt);
        });
    } else {
        document.getElementById('entryId').value = '';
        document.getElementById('entryTaskName').value = '';
        document.getElementById('entryStartDate').value = '';
        document.getElementById('entryEndDate').value = '';
        document.getElementById('entryAssignee').value = '';
        document.getElementById('entryProgress').value = '0';
        document.getElementById('entryStatus').value = 'planned';
        document.getElementById('entryNotes').value = '';
    }

    modal.classList.remove('hidden');
}

function closeEntryModal() { document.getElementById('entryModal').classList.add('hidden'); }

async function saveEntry() {
    const entryId = document.getElementById('entryId').value;
    const taskName = document.getElementById('entryTaskName').value.trim();
    if (!taskName) { showToast('작업명을 입력해주세요.', 'error'); return; }

    const data = {
        project_id: document.getElementById('entryProject').value,
        phase_id: document.getElementById('entryPhase').value,
        task_name: taskName,
        start_date: document.getElementById('entryStartDate').value,
        end_date: document.getElementById('entryEndDate').value,
        assignee: document.getElementById('entryAssignee').value || null,
        progress: parseInt(document.getElementById('entryProgress').value) || 0,
        status: document.getElementById('entryStatus').value,
        notes: document.getElementById('entryNotes').value || null
    };

    try {
        if (entryId) {
            await api(`/schedule/entries/${entryId}`, { method: 'PUT', body: JSON.stringify(data) });

            // Update dependencies
            const depSelect = document.getElementById('entryDependencies');
            const selectedDeps = Array.from(depSelect.selectedOptions).map(o => parseInt(o.value));
            const existingDeps = ganttData.dependencies.filter(d => d.entry_id === parseInt(entryId)).map(d => d.depends_on_entry_id);

            // Add new
            for (const depId of selectedDeps) {
                if (!existingDeps.includes(depId)) {
                    await api(`/schedule/entries/${entryId}/dependencies`, { method: 'POST', body: JSON.stringify({ depends_on_entry_id: depId }) });
                }
            }
            // Remove old
            for (const depId of existingDeps) {
                if (!selectedDeps.includes(depId)) {
                    await api(`/schedule/entries/${entryId}/dependencies/${depId}`, { method: 'DELETE' });
                }
            }

            showToast('공정표 항목이 수정되었습니다.');
        } else {
            const res = await api('/schedule/entries', { method: 'POST', body: JSON.stringify(data) });

            // Add dependencies for new entry
            const depSelect = document.getElementById('entryDependencies');
            const selectedDeps = Array.from(depSelect.selectedOptions).map(o => parseInt(o.value));
            for (const depId of selectedDeps) {
                await api(`/schedule/entries/${res.data.entry_id}/dependencies`, { method: 'POST', body: JSON.stringify({ depends_on_entry_id: depId }) });
            }

            showToast('공정표 항목이 추가되었습니다.');
        }
        closeEntryModal();
        await loadGantt();
    } catch (err) { showToast(err.message, 'error'); }
}

/* ===== Batch Modal ===== */
function openBatchModal() {
    populateSelect('batchProject', projects, 'project_id', p => `${p.job_no} ${p.project_name}`);
    populateSelect('batchPhase', phases, 'phase_id', p => p.phase_name);
    document.getElementById('batchStartDate').value = '';
    document.getElementById('batchTemplateList').innerHTML = '';
    loadBatchTemplates();
    document.getElementById('batchModal').classList.remove('hidden');
}

function closeBatchModal() { document.getElementById('batchModal').classList.add('hidden'); }

function loadBatchTemplates() {
    const phaseId = parseInt(document.getElementById('batchPhase').value);
    const filtered = templates.filter(t => t.phase_id === phaseId);
    const list = document.getElementById('batchTemplateList');
    if (filtered.length === 0) { list.innerHTML = '<p class="text-gray-500 text-sm">해당 단계에 템플릿이 없습니다.</p>'; return; }

    list.innerHTML = filtered.map((t, i) => `
        <div class="flex items-center gap-3 p-2 bg-gray-50 rounded-lg">
            <input type="checkbox" id="btmpl_${t.template_id}" checked class="w-4 h-4">
            <span class="flex-1 text-sm">${escapeHtml(t.task_name)}</span>
            <span class="text-xs text-gray-400">${t.default_duration_days}일</span>
            <input type="date" id="btmpl_start_${t.template_id}" class="input-field rounded px-2 py-1 text-xs w-32">
            <span class="text-xs text-gray-400">~</span>
            <input type="date" id="btmpl_end_${t.template_id}" class="input-field rounded px-2 py-1 text-xs w-32">
        </div>
    `).join('');

    recalcBatchDates();
}

function recalcBatchDates() {
    const baseStart = document.getElementById('batchStartDate').value;
    if (!baseStart) return;
    const phaseId = parseInt(document.getElementById('batchPhase').value);
    const filtered = templates.filter(t => t.phase_id === phaseId);
    let cursor = new Date(baseStart);
    for (const t of filtered) {
        const startEl = document.getElementById(`btmpl_start_${t.template_id}`);
        const endEl = document.getElementById(`btmpl_end_${t.template_id}`);
        if (startEl && endEl) {
            startEl.value = cursor.toISOString().split('T')[0];
            const endDate = new Date(cursor);
            endDate.setDate(endDate.getDate() + t.default_duration_days);
            endEl.value = endDate.toISOString().split('T')[0];
            cursor = new Date(endDate);
        }
    }
}

async function saveBatchEntries() {
    const projectId = document.getElementById('batchProject').value;
    const phaseId = document.getElementById('batchPhase').value;
    const filtered = templates.filter(t => t.phase_id === parseInt(phaseId));

    const entries = [];
    for (const t of filtered) {
        const cb = document.getElementById(`btmpl_${t.template_id}`);
        if (!cb || !cb.checked) continue;
        const startDate = document.getElementById(`btmpl_start_${t.template_id}`)?.value;
        const endDate = document.getElementById(`btmpl_end_${t.template_id}`)?.value;
        if (!startDate || !endDate) { showToast(`${t.task_name}의 날짜를 입력해주세요.`, 'error'); return; }
        entries.push({ task_name: t.task_name, start_date: startDate, end_date: endDate, display_order: t.display_order });
    }

    if (entries.length === 0) { showToast('추가할 항목이 없습니다.', 'error'); return; }

    try {
        await api('/schedule/entries/batch', { method: 'POST', body: JSON.stringify({ project_id: projectId, phase_id: phaseId, entries }) });
        showToast(`${entries.length}개 항목이 일괄 추가되었습니다.`);
        closeBatchModal();
        await loadGantt();
    } catch (err) { showToast(err.message, 'error'); }
}

/* ===== Milestone Modal ===== */
function openMilestoneModal(editId) {
    const modal = document.getElementById('milestoneModal');
    const isEdit = !!editId;
    document.getElementById('milestoneModalTitle').textContent = isEdit ? '마일스톤 수정' : '마일스톤 추가';

    populateSelect('milestoneProject', projects, 'project_id', p => `${p.job_no} ${p.project_name}`);

    if (isEdit) {
        const m = ganttData.milestones.find(ms => ms.milestone_id === editId);
        if (!m) return;
        document.getElementById('milestoneId').value = editId;
        document.getElementById('milestoneProject').value = m.project_id;
        document.getElementById('milestoneName').value = m.milestone_name;
        document.getElementById('milestoneDate').value = formatDate(m.milestone_date);
        document.getElementById('milestoneType').value = m.milestone_type;
        document.getElementById('milestoneStatus').value = m.status;
        document.getElementById('milestoneNotes').value = m.notes || '';
        // Load entry options for project
        loadMilestoneEntries(m.project_id, m.entry_id);
    } else {
        document.getElementById('milestoneId').value = '';
        document.getElementById('milestoneName').value = '';
        document.getElementById('milestoneDate').value = '';
        document.getElementById('milestoneType').value = 'deadline';
        document.getElementById('milestoneStatus').value = 'upcoming';
        document.getElementById('milestoneNotes').value = '';
        document.getElementById('milestoneEntry').innerHTML = '<option value="">없음</option>';
    }

    // Update entry list on project change
    document.getElementById('milestoneProject').onchange = function() { loadMilestoneEntries(this.value); };

    modal.classList.remove('hidden');
}

function loadMilestoneEntries(projectId, selectedEntryId) {
    const sel = document.getElementById('milestoneEntry');
    sel.innerHTML = '<option value="">없음</option>';
    const projEntries = ganttData.entries.filter(e => e.project_id === parseInt(projectId));
    projEntries.forEach(e => {
        const opt = document.createElement('option');
        opt.value = e.entry_id;
        opt.textContent = `${e.phase_name} > ${e.task_name}`;
        if (selectedEntryId && e.entry_id === selectedEntryId) opt.selected = true;
        sel.appendChild(opt);
    });
}

function closeMilestoneModal() { document.getElementById('milestoneModal').classList.add('hidden'); }

async function saveMilestone() {
    const milestoneId = document.getElementById('milestoneId').value;
    const data = {
        project_id: document.getElementById('milestoneProject').value,
        milestone_name: document.getElementById('milestoneName').value.trim(),
        milestone_date: document.getElementById('milestoneDate').value,
        milestone_type: document.getElementById('milestoneType').value,
        status: document.getElementById('milestoneStatus').value,
        entry_id: document.getElementById('milestoneEntry').value || null,
        notes: document.getElementById('milestoneNotes').value || null
    };

    if (!data.milestone_name || !data.milestone_date) { showToast('마일스톤명과 날짜를 입력해주세요.', 'error'); return; }

    try {
        if (milestoneId) {
            await api(`/schedule/milestones/${milestoneId}`, { method: 'PUT', body: JSON.stringify(data) });
            showToast('마일스톤이 수정되었습니다.');
        } else {
            await api('/schedule/milestones', { method: 'POST', body: JSON.stringify(data) });
            showToast('마일스톤이 추가되었습니다.');
        }
        closeMilestoneModal();
        await loadGantt();
    } catch (err) { showToast(err.message, 'error'); }
}

/* ===== Utility ===== */
function populateSelect(selectId, items, valueField, labelFn) {
    const sel = document.getElementById(selectId);
    const oldVal = sel.value;
    sel.innerHTML = items.map(item => `<option value="${item[valueField]}">${escapeHtml(labelFn(item))}</option>`).join('');
    if (oldVal && sel.querySelector(`option[value="${oldVal}"]`)) sel.value = oldVal;
}

function loadTemplateOptions(selectId, phaseId) {
    const sel = document.getElementById(selectId);
    sel.innerHTML = '<option value="">직접 입력</option>';
    templates.filter(t => t.phase_id === parseInt(phaseId)).forEach(t => {
        sel.innerHTML += `<option value="${t.template_id}">${escapeHtml(t.task_name)} (${t.default_duration_days}일)</option>`;
    });
}
