{{define "title"}}QFS Проект{{end}}

{{define "content"}}
<div class="space-y-4">
    <div class="flex items-start justify-between gap-3">
        <div class="flex flex-wrap items-center gap-2 sm:gap-3 min-w-0">
            <a href="/projects" class="shrink-0 text-gray-500 hover:text-gray-700" title="Все проекты">
                <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9.75L12 3l9 6.75v9A2.25 2.25 0 0118.75 21h-13.5A2.25 2.25 0 013 18.75v-9z"></path>
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 21v-6h6v6"></path>
                </svg>
            </a>
            <div class="flex flex-wrap items-center gap-2 text-xl sm:text-2xl font-bold min-w-0">
                <a id="project-code-link" href="/projects" class="min-w-0 truncate text-blue-700 hover:underline">
                    <span id="project-code">—</span>
                </a>
                <span class="text-gray-400 shrink-0">-</span>
                <div class="relative shrink-0">
                    <button id="project-variant-button" type="button" class="inline-flex items-center gap-2 text-sm sm:text-base font-medium px-3 py-1.5 rounded-lg bg-gray-100 hover:bg-gray-200 border border-gray-200">
                        <span id="project-variant-label">main</span>
                        <svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
                        </svg>
                    </button>
                    <div id="project-variant-menu" class="absolute left-0 mt-2 min-w-[10rem] rounded-lg border border-gray-200 bg-white shadow-lg hidden z-10">
                        <div id="project-variant-list" class="py-1"></div>
                    </div>
                </div>
                <button onclick="openNewVariantModal()" class="inline-flex w-full sm:w-auto justify-center items-center px-3 py-1.5 text-sm font-medium bg-purple-600 text-white rounded-lg hover:bg-purple-700">
                    + Вариант
                </button>
                <button onclick="openVariantActionModal()" class="inline-flex w-full sm:w-auto justify-center items-center px-3 py-1.5 text-sm font-medium bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">
                    Действия с вариантом
                </button>
            </div>
        </div>
    </div>

    <div id="action-buttons" class="mt-4 grid grid-cols-1 sm:grid-cols-6 gap-3">
        <button onclick="openCreateModal()" class="py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium">
            + Конфигурация
        </button>
        <button id="refresh-all-prices-btn" onclick="refreshPrices()" class="py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium">
            Обновить цены
        </button>
        <button onclick="openVendorImportModal()" class="py-2 bg-amber-600 text-white rounded-lg hover:bg-amber-700 font-medium">
            Импорт
        </button>
        <button onclick="openExportModal()" class="py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 font-medium">
            Экспорт CSV
        </button>
        <button onclick="openProjectSettingsModal()" class="py-2 bg-gray-700 text-white rounded-lg hover:bg-gray-800 font-medium">
            Параметры
        </button>
        <button id="delete-variant-btn" onclick="deleteVariant()" class="py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 font-medium hidden">
            Удалить вариант
        </button>
    </div>
    <div class="mt-2">
        <a id="tracker-link" href="https://tracker.yandex.ru/OPS-1933" target="_blank" rel="noopener noreferrer" class="text-sm text-blue-600 hover:text-blue-800 hover:underline">
            открыть в трекере
        </a>
    </div>

    <div class="mt-4 inline-flex rounded-lg border border-gray-200 overflow-hidden">
        <button id="status-active-btn" onclick="setConfigStatusMode('active')" class="px-4 py-2 text-sm font-medium bg-blue-600 text-white">
            Активные
        </button>
        <button id="status-archived-btn" onclick="setConfigStatusMode('archived')" class="px-4 py-2 text-sm font-medium bg-white text-gray-700 hover:bg-gray-50 border-l border-gray-200">
            Архив
        </button>
    </div>

    <div id="configs-list">
        <div class="text-center py-8 text-gray-500">Загрузка...</div>
    </div>
</div>

<div id="create-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
    <div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 p-6">
        <h2 class="text-xl font-semibold mb-4">Новая конфигурация в проекте</h2>
        <div class="space-y-4">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Тип оборудования</label>
                <div class="inline-flex rounded-lg border border-gray-200 overflow-hidden w-full">
                    <button type="button" id="pd-type-server-btn" onclick="pdSetCreateType('server')"
                            class="flex-1 py-2 text-sm font-medium bg-blue-600 text-white">
                        Сервер
                    </button>
                    <button type="button" id="pd-type-storage-btn" onclick="pdSetCreateType('storage')"
                            class="flex-1 py-2 text-sm font-medium bg-white text-gray-700 hover:bg-gray-50 border-l border-gray-200">
                        СХД
                    </button>
                </div>
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Название конфигурации</label>
                <input type="text" id="create-name" placeholder="Например: OPP-2026-001"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
            </div>
        </div>
        <div class="flex justify-end space-x-3 mt-6">
            <button onclick="closeCreateModal()" class="px-4 py-2 text-gray-600 hover:text-gray-800">Отмена</button>
            <button onclick="createConfig()" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Создать</button>
        </div>
    </div>
</div>

<div id="vendor-import-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
    <div class="bg-white rounded-lg shadow-xl w-full max-w-lg mx-4 p-6">
        <h2 class="text-xl font-semibold mb-4">Импорт конфигураций</h2>
        <div class="space-y-4">
            <div class="text-sm text-gray-600">
                Поддерживаемые форматы: CFXML-выгрузка вендора (.xml), собственный CSV-экспорт QuoteForge (.csv), текстовый BOM Inspur (.txt).
            </div>
            <div>
                <label for="vendor-import-file" class="block text-sm font-medium text-gray-700 mb-1">Файл</label>
                <input id="vendor-import-file" type="file" accept=".xml,.csv,.txt,text/xml,application/xml,text/csv,text/plain"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-amber-500 focus:border-amber-500">
            </div>
            <div id="vendor-import-status" class="hidden text-sm rounded border px-3 py-2"></div>
        </div>
        <div class="flex justify-end space-x-3 mt-6">
            <button onclick="closeVendorImportModal()" class="px-4 py-2 text-gray-600 hover:text-gray-800">Отмена</button>
            <button id="vendor-import-submit" onclick="importVendorWorkspace()" class="px-4 py-2 bg-amber-600 text-white rounded hover:bg-amber-700">Импортировать</button>
        </div>
    </div>
</div>

<div id="project-export-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
    <div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 p-6">
        <h2 class="text-xl font-semibold mb-5">Экспорт CSV</h2>
        <div class="space-y-5">

            <!-- Section 1: Артикул -->
            <div>
                <p class="text-xs font-semibold uppercase tracking-wide text-gray-500 mb-2">Артикул</p>
                <div class="space-y-2">
                    <label class="flex items-center gap-3 text-sm text-gray-700">
                        <input id="export-col-lot" type="checkbox" class="rounded border-gray-300" checked>
                        <span>LOT</span>
                    </label>
                    <label class="flex items-center gap-3 text-sm text-gray-700">
                        <input id="export-col-bom" type="checkbox" class="rounded border-gray-300">
                        <span>BOM <span class="text-gray-400 font-normal">(строки по BOM, иначе по Estimate)</span></span>
                    </label>
                </div>
            </div>

            <!-- Section 2: Цены -->
            <div>
                <p class="text-xs font-semibold uppercase tracking-wide text-gray-500 mb-2">Цены</p>
                <div class="space-y-2">
                    <label class="flex items-center gap-3 text-sm text-gray-700">
                        <input id="export-col-estimate" type="checkbox" class="rounded border-gray-300" checked>
                        <span>Est</span>
                    </label>
                    <label class="flex items-center gap-3 text-sm text-gray-700">
                        <input id="export-col-stock" type="checkbox" class="rounded border-gray-300">
                        <span>Stock</span>
                    </label>
                    <label class="flex items-center gap-3 text-sm text-gray-700">
                        <input id="export-col-competitor" type="checkbox" class="rounded border-gray-300">
                        <span>Конкуренты</span>
                    </label>
                </div>
            </div>

            <!-- Section 3: Базис поставки -->
            <div>
                <p class="text-xs font-semibold uppercase tracking-wide text-gray-500 mb-2">Базис поставки</p>
                <div class="flex gap-6">
                    <label class="flex items-center gap-2 text-sm text-gray-700 cursor-pointer">
                        <input type="radio" name="export-basis" value="fob" class="border-gray-300" checked>
                        <span class="font-medium">FOB</span>
                        <span class="text-gray-400">— Цена покупки</span>
                    </label>
                    <label class="flex items-center gap-2 text-sm text-gray-700 cursor-pointer">
                        <input type="radio" name="export-basis" value="ddp" class="border-gray-300">
                        <span class="font-medium">DDP</span>
                        <span class="text-gray-400">— Цена продажи ×1,3</span>
                    </label>
                </div>
            </div>

            <div id="project-export-status" class="hidden text-sm rounded border px-3 py-2"></div>
        </div>
        <div class="flex justify-end space-x-3 mt-6">
            <button onclick="closeExportModal()" class="px-4 py-2 text-gray-600 hover:text-gray-800">Отмена</button>
            <button id="project-export-submit" onclick="exportProject()" class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">Скачать CSV</button>
        </div>
    </div>
</div>

<div id="new-variant-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
    <div class="bg-white rounded-lg shadow-xl w-full max-w-lg p-6">
        <h2 class="text-xl font-semibold mb-4">Новый вариант</h2>
        <div class="space-y-4">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Код проекта</label>
                <div id="new-variant-code" class="px-3 py-2 bg-gray-50 border rounded text-sm text-gray-700">—</div>
            </div>
            <div>
                <label for="new-variant-name" class="block text-sm font-medium text-gray-700 mb-1">Название (необязательно)</label>
                <input id="new-variant-name" type="text" placeholder="Например: Lenovo"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
            </div>
            <div>
                <label for="new-variant-value" class="block text-sm font-medium text-gray-700 mb-1">Вариант</label>
                <input id="new-variant-value" type="text" placeholder="Например: B200"
                       pattern="[A-Za-z0-9._-]+"
                       title="Только буквы, цифры, дефис, точка, подчёркивание"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
                <div class="text-xs text-gray-500 mt-1">Буквы, цифры, дефис, точка, подчёркивание. Используется в URL: /КОД/Вариант.</div>
            </div>
        </div>
        <div class="mt-6 flex justify-end gap-2">
            <button onclick="closeNewVariantModal()" class="px-4 py-2 text-gray-700 bg-gray-100 rounded hover:bg-gray-200">Отмена</button>
            <button onclick="createNewVariant()" class="px-4 py-2 text-white bg-purple-600 rounded hover:bg-purple-700">Создать</button>
        </div>
    </div>
</div>

<div id="variant-action-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
    <div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 p-6">
        <h2 class="text-xl font-semibold mb-4">Действия с вариантом</h2>
        <div class="space-y-4">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Название</label>
                <input type="text" id="variant-action-name"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
            </div>
            <label class="flex items-center gap-2 text-sm text-gray-700">
                <input type="checkbox" id="variant-action-copy" class="rounded border-gray-300">
                Создать копию
            </label>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Код проекта</label>
                <input type="text" id="variant-action-code"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
            </div>
            <input type="hidden" id="variant-action-current-name">
            <input type="hidden" id="variant-action-current-code">
        </div>
        <div class="flex justify-end space-x-3 mt-6">
            <button onclick="closeVariantActionModal()" class="px-4 py-2 text-gray-600 hover:text-gray-800">Отмена</button>
            <button onclick="saveVariantAction()" class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700">Сохранить</button>
        </div>
    </div>
</div>

<div id="config-action-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
    <div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 p-6">
        <h2 class="text-xl font-semibold mb-4">Действия с конфигурацией</h2>
        <div class="space-y-4">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Название</label>
                <input type="text" id="config-action-name"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
            </div>
            <label class="flex items-center gap-2 text-sm text-gray-700">
                <input type="checkbox" id="config-action-copy" class="rounded border-gray-300">
                Создать копию
            </label>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Проект</label>
                <input id="config-action-project-input"
                       list="config-action-project-options"
                       placeholder="Начните вводить проект"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
                <datalist id="config-action-project-options"></datalist>
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Вариант</label>
                <input id="config-action-variant-input"
                       list="config-action-variant-options"
                       placeholder="Выберите вариант"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
                <datalist id="config-action-variant-options"></datalist>
            </div>
            <input type="hidden" id="config-action-uuid">
            <input type="hidden" id="config-action-current-name">
            <input type="hidden" id="config-action-current-project">
        </div>
        <div class="flex justify-end space-x-3 mt-6">
            <button onclick="closeConfigActionModal()" class="px-4 py-2 text-gray-600 hover:text-gray-800">Отмена</button>
            <button onclick="saveConfigAction()" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Сохранить</button>
        </div>
    </div>
</div>

<div id="project-settings-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
    <div class="bg-white rounded-lg shadow-xl w-full max-w-md mx-4 p-6">
        <h2 class="text-xl font-semibold mb-4">Параметры проекта</h2>
        <div class="space-y-4">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Код проекта</label>
                <input type="text" id="project-settings-code"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Вариант (необязательно)</label>
                <input type="text" id="project-settings-variant"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Название проекта</label>
                <input type="text" id="project-settings-name"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Ссылка для "открыть в трекере"</label>
                <input type="text" id="project-settings-tracker-url" placeholder="https://tracker.example.com/PROJ-123"
                       class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
                <div class="text-xs text-gray-500 mt-1">Оставьте пустым, чтобы скрыть ссылку.</div>
            </div>
        </div>
        <div class="flex justify-end space-x-3 mt-6">
            <button onclick="closeProjectSettingsModal()" class="px-4 py-2 text-gray-600 hover:text-gray-800">Отмена</button>
            <button onclick="saveProjectSettings()" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Сохранить</button>
        </div>
    </div>
</div>

<script>
const projectUUID = '{{.ProjectUUID}}';
let configStatusMode = 'active';
let project = null;
let allConfigs = [];
let dragConfigUUID = '';
let isReorderingConfigs = false;
let projectVariants = [];
let projectsCatalog = [];
let variantMenuInitialized = false;

function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text || '';
    return div.innerHTML;
}

function formatMoneyNoDecimals(value) {
    const safe = Number.isFinite(Number(value)) ? Number(value) : 0;
    return '$' + Math.round(safe).toLocaleString('ru-RU');
}

function resolveProjectTrackerURL(projectData) {
    if (!projectData) return '';
    const explicitURL = (projectData.tracker_url || '').trim();
    return explicitURL;
}

function formatProjectTitle(projectData) {
    if (!projectData) return 'Проект';
    const code = (projectData.code || '').trim();
    const name = (projectData.name || '').trim();
    const variant = (projectData.variant || '').trim();
    if (!code) return name || 'Проект';
    if (variant) {
        return code + ': (' + variant + ') ' + (name || '');
    }
    return code + ': ' + (name || '');
}

function normalizeVariantLabel(variant) {
    const trimmed = (variant || '').trim();
    return trimmed === '' ? 'main' : trimmed;
}

async function loadVariantsForCode(code) {
    try {
        const resp = await fetch('/api/projects/all');
        if (!resp.ok) return;
        const data = await resp.json();
        const allProjects = Array.isArray(data) ? data : (data.projects || []);
        projectsCatalog = allProjects.filter(p => p && p.uuid && p.is_active !== false);
        if (!code) {
            projectVariants = [];
            return;
        }
        projectVariants = allProjects
            .filter(p => (p.code || '').trim() === code && p.is_active !== false)
            .map(p => ({uuid: p.uuid, variant: (p.variant || '').trim()}));
        projectVariants.sort((a, b) => normalizeVariantLabel(a.variant).localeCompare(normalizeVariantLabel(b.variant)));
    } catch (e) {
        // ignore
    }
}

function renderVariantSelect() {
    const list = document.getElementById('project-variant-list');
    const menu = document.getElementById('project-variant-menu');
    const button = document.getElementById('project-variant-button');
    const label = document.getElementById('project-variant-label');
    const codeLink = document.getElementById('project-code-link');
    if (!list || !menu || !button || !label) return;
    list.innerHTML = '';
    const variants = projectVariants.length ? projectVariants : [{uuid: projectUUID, variant: (project && project.variant) || ''}];
    let mainUUID = '';
    variants.forEach(item => {
        const variantLabel = normalizeVariantLabel(item.variant);
        if (variantLabel === 'main' && !mainUUID) {
            mainUUID = item.uuid;
        }
        const option = document.createElement('button');
        option.type = 'button';
        option.className = 'w-full text-left px-3 py-2 text-sm hover:bg-gray-50';
        if (item.uuid === projectUUID) {
            option.className += ' font-semibold text-gray-900';
            label.textContent = variantLabel;
            document.title = (project && project.code ? project.code : '—') + ' / ' + variantLabel + ' — OFS';
        }
        option.textContent = variantLabel;
        option.onclick = function() {
            menu.classList.add('hidden');
            if (item.uuid && item.uuid !== projectUUID) {
                window.location.href = '/projects/' + item.uuid;
            }
        };
        list.appendChild(option);
    });

    if (codeLink) {
        const targetMain = mainUUID || projectUUID;
        codeLink.href = '/projects/' + targetMain;
    }

    if (!variantMenuInitialized) {
        button.onclick = function(e) {
            e.stopPropagation();
            menu.classList.toggle('hidden');
        };
        document.addEventListener('click', function() {
            menu.classList.add('hidden');
        });
        menu.addEventListener('click', function(e) {
            e.stopPropagation();
        });
        variantMenuInitialized = true;
    }
}

function setConfigStatusMode(mode) {
    if (mode !== 'active' && mode !== 'archived') return;
    configStatusMode = mode;
    applyStatusModeUI();
    loadConfigs();
}

function applyStatusModeUI() {
    const activeBtn = document.getElementById('status-active-btn');
    const archivedBtn = document.getElementById('status-archived-btn');
    const actionButtons = document.getElementById('action-buttons');

    if (configStatusMode === 'archived') {
        activeBtn.className = 'px-4 py-2 text-sm font-medium bg-white text-gray-700 hover:bg-gray-50';
        archivedBtn.className = 'px-4 py-2 text-sm font-medium bg-blue-600 text-white border-l border-gray-200';
        actionButtons.classList.add('hidden');
    } else {
        activeBtn.className = 'px-4 py-2 text-sm font-medium bg-blue-600 text-white';
        archivedBtn.className = 'px-4 py-2 text-sm font-medium bg-white text-gray-700 hover:bg-gray-50 border-l border-gray-200';
        actionButtons.classList.remove('hidden');
    }
}

function renderConfigs(configs) {
    const emptyText = configStatusMode === 'archived' ? 'Архив пуст' : 'Нет конфигураций в проекте';
    if (configs.length === 0) {
        document.getElementById('configs-list').innerHTML =
            '<div class="bg-white rounded-lg shadow p-8 text-center text-gray-500">' + emptyText + '</div>';
        return;
    }

    let totalSum = 0;
    let html = '<div class="bg-white rounded-lg shadow overflow-hidden"><table class="w-full">';
    html += '<thead class="bg-gray-50"><tr>';
    html += '<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Line</th>';
    html += '<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">модель</th>';
    html += '<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Название</th>';
    html += '<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Автор</th>';
    html += '<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Цена за 1 шт.</th>';
    html += '<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Кол-во</th>';
    html += '<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">Стоимость</th>';
    html += '<th class="px-2 py-3 text-center text-xs font-medium text-gray-500 uppercase w-12"></th>';
    html += '<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">Действия</th>';
    html += '</tr></thead><tbody class="divide-y" id="project-configs-tbody">';

    configs.forEach((c, idx) => {
        const total = c.total_price || 0;
        const serverCount = c.server_count || 1;
        const author = c.owner_username || (c.user && c.user.username) || '—';
        const unitPrice = serverCount > 0 ? (total / serverCount) : 0;
        const lineValue = (idx + 1) * 10;
        const serverModel = (c.server_model || '').trim() || '—';
        totalSum += total;

        const draggable = configStatusMode === 'active' ? 'true' : 'false';
        html += '<tr class="hover:bg-gray-50" draggable="' + draggable + '" data-config-uuid="' + c.uuid + '" ondragstart="onConfigDragStart(event)" ondragover="onConfigDragOver(event)" ondragleave="onConfigDragLeave(event)" ondrop="onConfigDrop(event)" ondragend="onConfigDragEnd(event)">';
        if (configStatusMode === 'active') {
            html += '<td class="px-4 py-3 text-sm text-gray-500">';
            html += '<span class="inline-flex items-center gap-2"><span class="drag-handle text-gray-400 hover:text-gray-700 cursor-grab active:cursor-grabbing select-none" title="Перетащить для изменения порядка" aria-label="Перетащить">';
            html += '<svg class="w-4 h-4 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 6h.01M8 12h.01M8 18h.01M16 6h.01M16 12h.01M16 18h.01"></path></svg>';
            html += '</span><span>' + lineValue + '</span></span></td>';
        } else {
            html += '<td class="px-4 py-3 text-sm text-gray-500">' + lineValue + '</td>';
        }
        html += '<td class="px-4 py-3 text-sm text-gray-500">' + escapeHtml(serverModel) + '</td>';
        if (configStatusMode === 'archived') {
            html += '<td class="px-4 py-3 text-sm font-medium text-gray-700">' + escapeHtml(c.name) + '</td>';
        } else {
            html += '<td class="px-4 py-3 text-sm font-medium"><a href="/configurator?uuid=' + c.uuid + '" class="text-blue-600 hover:text-blue-800 hover:underline">' + escapeHtml(c.name) + '</a></td>';
        }
        html += '<td class="px-4 py-3 text-sm text-gray-500">' + escapeHtml(author) + '</td>';
        html += '<td class="px-4 py-3 text-sm text-gray-500">' + formatMoneyNoDecimals(unitPrice) + '</td>';
        if (configStatusMode === 'archived') {
            html += '<td class="px-4 py-3 text-sm text-gray-500">' + serverCount + '</td>';
        } else {
            html += '<td class="px-4 py-3 text-sm text-gray-500"><input type="number" min="1" value="' + serverCount + '" class="w-16 px-1 py-0.5 border rounded text-center text-sm" data-uuid="' + c.uuid + '" data-prev="' + serverCount + '" onchange="updateConfigServerCount(this)"></td>';
        }
        html += '<td class="px-4 py-3 text-sm text-right" data-total-uuid="' + c.uuid + '">' + formatMoneyNoDecimals(total) + '</td>';
        html += '<td class="px-2 py-3 text-sm text-center text-gray-500 w-12">main</td>';
        html += '<td class="px-4 py-3 text-sm text-right whitespace-nowrap"><div class="inline-flex items-center justify-end gap-2">';
        if (configStatusMode === 'archived') {
            html += '<button onclick="reactivateConfig(\'' + c.uuid + '\')" class="text-emerald-600 hover:text-emerald-800" title="Восстановить">';
            html += '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg></button>';
        } else {
            html += '<a href="/configs/' + c.uuid + '/revisions" class="text-purple-600 hover:text-purple-800 inline-block" title="Ревизии">';
            html += '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg></a>';
            html += '<button onclick="openConfigActionModal(\'' + c.uuid + '\', \'' + escapeHtml(c.name).replace(/'/g, "\\'") + '\', \'' + (c.project_uuid || projectUUID) + '\')" class="text-indigo-600 hover:text-indigo-800" title="Переименовать / копировать / переместить">';
            html += '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7h16M4 12h16M4 17h16"></path></svg></button>';
            html += '<button onclick="deleteConfig(\'' + c.uuid + '\')" class="text-red-600 hover:text-red-800" title="В архив">';
            html += '<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg></button>';
        }
        html += '</div></td></tr>';
    });

    html += '</tbody>';
    html += '<tfoot class="bg-gray-50 border-t">';
    html += '<tr>';
    html += '<td class="px-4 py-3 text-sm font-medium text-gray-700" colspan="5">Итого по проекту</td>';
    html += '<td class="px-4 py-3 text-sm text-gray-700">' + configs.length + '</td>';
    html += '<td class="px-4 py-3 text-sm text-right font-semibold text-gray-900" data-footer-total="1">' + formatMoneyNoDecimals(totalSum) + '</td>';
    html += '<td class="px-4 py-3"></td>';
    html += '<td class="px-4 py-3"></td>';
    html += '<td class="px-4 py-3"></td>';
    html += '</tr>';
    html += '</tfoot>';
    html += '</table></div>';
    document.getElementById('configs-list').innerHTML = html;
}

async function loadProject() {
    const resp = await fetch('/api/projects/' + projectUUID);
    if (!resp.ok) {
        document.getElementById('configs-list').innerHTML = '<div class="bg-white rounded-lg shadow p-8 text-center text-red-600">Проект не найден</div>';
        return false;
    }
    project = await resp.json();
    document.getElementById('project-code').textContent = project.code || '—';
    await loadVariantsForCode(project.code || '');
    renderVariantSelect();
    const trackerLink = document.getElementById('tracker-link');
    if (trackerLink) {
        if (project && project.is_system) {
            trackerLink.classList.add('hidden');
            return true;
        }
        const trackerURL = resolveProjectTrackerURL(project);
        if (trackerURL) {
            trackerLink.href = trackerURL;
            trackerLink.classList.remove('hidden');
        } else {
            trackerLink.classList.add('hidden');
        }
    }
    return true;
}

async function loadConfigs() {
    try {
        const resp = await fetch('/api/projects/' + projectUUID + '/configs?status=' + configStatusMode);
        if (!resp.ok) {
            document.getElementById('configs-list').innerHTML =
                '<div class="bg-white rounded-lg shadow p-8 text-center text-red-600">Ошибка загрузки</div>';
            return;
        }

        const data = await resp.json();
        allConfigs = (data.configurations || []);
        renderConfigs(allConfigs);
    } catch (e) {
        document.getElementById('configs-list').innerHTML =
            '<div class="bg-white rounded-lg shadow p-8 text-center text-red-600">Ошибка загрузки</div>';
    }
}

let pdCreateConfigType = 'server';

function pdSetCreateType(type) {
    pdCreateConfigType = type;
    document.getElementById('pd-type-server-btn').className = 'flex-1 py-2 text-sm font-medium ' +
        (type === 'server' ? 'bg-blue-600 text-white' : 'bg-white text-gray-700 hover:bg-gray-50');
    document.getElementById('pd-type-storage-btn').className = 'flex-1 py-2 text-sm font-medium border-l border-gray-200 ' +
        (type === 'storage' ? 'bg-blue-600 text-white' : 'bg-white text-gray-700 hover:bg-gray-50');
}

function openCreateModal() {
    pdCreateConfigType = 'server';
    pdSetCreateType('server');
    document.getElementById('create-name').value = '';
    document.getElementById('create-modal').classList.remove('hidden');
    document.getElementById('create-modal').classList.add('flex');
    document.getElementById('create-name').focus();
}

function openNewVariantModal() {
    if (!project) return;
    document.getElementById('new-variant-code').textContent = (project.code || '').trim() || '—';
    document.getElementById('new-variant-name').value = project.name || '';
    document.getElementById('new-variant-value').value = '';
    document.getElementById('new-variant-modal').classList.remove('hidden');
    document.getElementById('new-variant-modal').classList.add('flex');
    document.getElementById('new-variant-value').focus();
}

function closeNewVariantModal() {
    document.getElementById('new-variant-modal').classList.add('hidden');
    document.getElementById('new-variant-modal').classList.remove('flex');
}

function openVariantActionModal() {
    if (!project) return;
    const currentName = (project.variant || '').trim();
    const currentCode = (project.code || '').trim();
    document.getElementById('variant-action-current-name').value = currentName;
    document.getElementById('variant-action-current-code').value = currentCode;
    document.getElementById('variant-action-name').value = currentName;
    document.getElementById('variant-action-code').value = currentCode;
    document.getElementById('variant-action-copy').checked = false;
    document.getElementById('variant-action-modal').classList.remove('hidden');
    document.getElementById('variant-action-modal').classList.add('flex');
    const nameInput = document.getElementById('variant-action-name');
    nameInput.focus();
    nameInput.select();
}

function closeVariantActionModal() {
    document.getElementById('variant-action-modal').classList.add('hidden');
    document.getElementById('variant-action-modal').classList.remove('flex');
}

function findUniqueVariantActionName(baseName, targetCode, excludeProjectUUID) {
    const cleanedBase = (baseName || '').trim();
    if (!cleanedBase || normalizeVariantLabel(cleanedBase).toLowerCase() === 'main') {
        return {error: 'Имя варианта не должно быть пустым и не может быть main'};
    }

    const code = (targetCode || '').trim();
    const used = new Set(
        projectsCatalog
            .filter(p => (p.code || '').trim().toLowerCase() === code.toLowerCase())
            .filter(p => !excludeProjectUUID || p.uuid !== excludeProjectUUID)
            .map(p => ((p.variant || '').trim()).toLowerCase())
    );

    if (!used.has(cleanedBase.toLowerCase())) {
        return {name: cleanedBase, changed: false};
    }

    let candidate = cleanedBase + '_копия';
    let suffix = 2;
    while (used.has(candidate.toLowerCase())) {
        candidate = cleanedBase + '_копия' + suffix;
        suffix++;
    }
    return {name: candidate, changed: true};
}

async function resolveUniqueConfigActionName(baseName, targetProjectUUID, excludeConfigUUID) {
    const cleanedBase = (baseName || '').trim();
    if (!cleanedBase) {
        return {error: 'Введите название'};
    }

    let configs = [];
    if (targetProjectUUID === projectUUID) {
        configs = Array.isArray(allConfigs) ? allConfigs : [];
    } else {
        const resp = await fetch('/api/projects/' + targetProjectUUID + '/configs?status=all');
        if (!resp.ok) {
            return {error: 'Не удалось проверить конфигурации целевого проекта'};
        }
        const data = await resp.json().catch(() => ({}));
        configs = Array.isArray(data.configurations) ? data.configurations : [];
    }

    const used = new Set(
        configs
            .filter(cfg => !excludeConfigUUID || cfg.uuid !== excludeConfigUUID)
            .map(cfg => (cfg.name || '').trim().toLowerCase())
    )

    if (!used.has(cleanedBase.toLowerCase())) {
        return {name: cleanedBase, changed: false};
    }

    let candidate = cleanedBase + '_копия';
    let suffix = 2;
    while (used.has(candidate.toLowerCase())) {
        candidate = cleanedBase + '_копия' + suffix;
        suffix++;
    }
    return {name: candidate, changed: true};
}

async function cloneVariantConfigurations(targetProjectUUID) {
    const listResp = await fetch('/api/projects/' + projectUUID + '/configs');
    if (!listResp.ok) {
        throw new Error('Не удалось загрузить конфигурации варианта');
    }
    const listData = await listResp.json().catch(() => ({}));
    const configs = Array.isArray(listData.configurations) ? listData.configurations : [];
    for (const cfg of configs) {
        const cloneResp = await fetch('/api/projects/' + targetProjectUUID + '/configs/' + cfg.uuid + '/clone', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({name: cfg.name})
        });
        if (!cloneResp.ok) {
            throw new Error('Не удалось скопировать конфигурацию «' + (cfg.name || 'без названия') + '»');
        }
    }
}

async function saveVariantAction() {
    if (!project) return;
    const notify = (message, type) => {
        if (typeof showToast === 'function') {
            showToast(message, type || 'success');
        } else {
            alert(message);
        }
    };

    const currentName = document.getElementById('variant-action-current-name').value.trim();
    const currentCode = document.getElementById('variant-action-current-code').value.trim();
    const rawName = document.getElementById('variant-action-name').value.trim();
    const code = document.getElementById('variant-action-code').value.trim();
    const copy = document.getElementById('variant-action-copy').checked;

    if (!code) {
        notify('Введите код проекта', 'error');
        return;
    }
    const uniqueNameResult = findUniqueVariantActionName(rawName, code, copy ? '' : projectUUID);
    if (uniqueNameResult.error) {
        notify(uniqueNameResult.error, 'error');
        return;
    }
    const name = uniqueNameResult.name;
    if (uniqueNameResult.changed) {
        document.getElementById('variant-action-name').value = name;
        notify('Имя варианта занято, использовано ' + name, 'success');
    }

    if (copy) {
        const createResp = await fetch('/api/projects', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({
                code: code,
                variant: name,
                name: project.name || null,
                tracker_url: (project.tracker_url || '').trim()
            })
        });
        if (!createResp.ok) {
            if (createResp.status === 400) {
                notify('Имя варианта не может быть main', 'error');
                return;
            }
            if (createResp.status === 409) {
                notify('Вариант с таким кодом и значением уже существует', 'error');
                return;
            }
            notify('Не удалось создать копию варианта', 'error');
            return;
        }
        const created = await createResp.json().catch(() => null);
        if (!created || !created.uuid) {
            notify('Не удалось создать копию варианта', 'error');
            return;
        }
        try {
            await cloneVariantConfigurations(created.uuid);
        } catch (err) {
            notify(err.message || 'Вариант создан, но конфигурации не скопированы полностью', 'error');
            window.location.href = '/projects/' + created.uuid;
            return;
        }
        closeVariantActionModal();
        notify('Копия варианта создана', 'success');
        window.location.href = '/projects/' + created.uuid;
        return;
    }

    const changed = name !== currentName || code !== currentCode;
    if (!changed) {
        closeVariantActionModal();
        return;
    }

    const updateResp = await fetch('/api/projects/' + projectUUID, {
        method: 'PUT',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({code: code, variant: name})
    });
    if (!updateResp.ok) {
        if (updateResp.status === 400) {
            notify('Имя варианта не может быть main', 'error');
            return;
        }
        if (updateResp.status === 409) {
            notify('Вариант с таким кодом и значением уже существует', 'error');
            return;
        }
        notify('Не удалось сохранить вариант', 'error');
        return;
    }

    closeVariantActionModal();
    await loadProject();
    await loadConfigs();
    updateDeleteVariantButton();
    notify('Вариант обновлён', 'success');
}

async function createNewVariant() {
    if (!project) return;
    const code = (project.code || '').trim();
    const variant = (document.getElementById('new-variant-value').value || '').trim();
    const nameRaw = (document.getElementById('new-variant-name').value || '').trim();
    if (!code || !variant) {
        showToast('Укажите вариант', 'error');
        return;
    }
    if (!/^[A-Za-z0-9._-]+$/.test(variant)) {
        showToast('Имя варианта содержит недопустимые символы. Разрешены: буквы, цифры, дефис, точка, подчёркивание.', 'error');
        return;
    }
    const payload = {
        code: code,
        variant: variant,
        name: nameRaw ? nameRaw : null
    };
    const resp = await fetch('/api/projects', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(payload)
    });
    if (!resp.ok) {
        const data = await resp.json().catch(() => ({}));
        showToast(data.error || 'Ошибка создания варианта', 'error');
        return;
    }
    const created = await resp.json().catch(() => null);
    closeNewVariantModal();
    showToast('Вариант создан', 'success');
    if (created && created.uuid) {
        window.location.href = '/projects/' + created.uuid;
        return;
    }
    loadProject();
    loadConfigs();
}

function closeCreateModal() {
    document.getElementById('create-modal').classList.add('hidden');
    document.getElementById('create-modal').classList.remove('flex');
}

function setVendorImportStatus(message, type) {
    const box = document.getElementById('vendor-import-status');
    if (!box) return;
    if (!message) {
        box.textContent = '';
        box.className = 'hidden text-sm rounded border px-3 py-2';
        return;
    }
    let classes = 'text-sm rounded border px-3 py-2 ';
    if (type === 'error') {
        classes += 'border-red-200 bg-red-50 text-red-700';
    } else if (type === 'success') {
        classes += 'border-emerald-200 bg-emerald-50 text-emerald-700';
    } else {
        classes += 'border-amber-200 bg-amber-50 text-amber-700';
    }
    box.className = classes;
    box.textContent = message;
}

function openVendorImportModal() {
    document.getElementById('vendor-import-file').value = '';
    setVendorImportStatus('', '');
    document.getElementById('vendor-import-submit').disabled = false;
    document.getElementById('vendor-import-submit').textContent = 'Импортировать';
    document.getElementById('vendor-import-modal').classList.remove('hidden');
    document.getElementById('vendor-import-modal').classList.add('flex');
}

function closeVendorImportModal() {
    document.getElementById('vendor-import-modal').classList.add('hidden');
    document.getElementById('vendor-import-modal').classList.remove('flex');
}

async function importVendorWorkspace() {
    const input = document.getElementById('vendor-import-file');
    const submit = document.getElementById('vendor-import-submit');
    if (!input || !input.files || !input.files[0]) {
        setVendorImportStatus('Выберите файл для импорта', 'error');
        return;
    }

    const formData = new FormData();
    formData.append('file', input.files[0]);
    submit.disabled = true;
    submit.textContent = 'Импорт...';
    setVendorImportStatus('Импортирую файл в проект...', 'info');

    try {
        const resp = await fetch('/api/projects/' + projectUUID + '/vendor-import', {
            method: 'POST',
            body: formData
        });
        const data = await resp.json().catch(() => ({}));
        if (!resp.ok) {
            throw new Error(data.error || 'Не удалось импортировать выгрузку');
        }

        const imported = Array.isArray(data.configs) ? data.configs : [];
        const importedNames = imported.map(c => c.name).filter(Boolean);
        const message = importedNames.length
            ? 'Импортировано ' + imported.length + ': ' + importedNames.join(', ')
            : 'Импортировано конфигураций: ' + (data.imported || 0);
        setVendorImportStatus(message, 'success');
        if (typeof showToast === 'function') {
            showToast('Импорт завершён', 'success');
        }
        await loadConfigs();
        setTimeout(closeVendorImportModal, 900);
    } catch (e) {
        setVendorImportStatus(e.message || 'Не удалось импортировать выгрузку', 'error');
        if (typeof showToast === 'function') {
            showToast(e.message || 'Не удалось импортировать выгрузку', 'error');
        }
    } finally {
        submit.disabled = false;
        submit.textContent = 'Импортировать';
    }
}

async function createConfig() {
    const name = document.getElementById('create-name').value.trim();
    if (!name) {
        alert('Введите название');
        return;
    }
    const resp = await fetch('/api/projects/' + projectUUID + '/configs', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({name: name, items: [], notes: '', server_count: 1, config_type: pdCreateConfigType})
    });
    if (!resp.ok) {
        alert('Не удалось создать конфигурацию');
        return;
    }
    closeCreateModal();
    loadConfigs();
}

async function deleteConfig(uuid) {
    if (!confirm('Переместить конфигурацию в архив?')) return;
    await fetch('/api/configs/' + uuid, {method: 'DELETE'});
    loadConfigs();
}

async function reactivateConfig(uuid) {
    const resp = await fetch('/api/configs/' + uuid + '/reactivate', {method: 'POST'});
    if (!resp.ok) {
        alert('Не удалось восстановить конфигурацию');
        return;
    }
    const moved = await fetch('/api/configs/' + uuid + '/project', {
        method: 'PATCH',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({project_uuid: projectUUID})
    });
    if (!moved.ok) {
        alert('Конфигурация восстановлена, но не удалось вернуть в проект');
    }
    loadConfigs();
}

function projectCodeEntries() {
    const byCode = new Map();
    projectsCatalog.forEach(p => {
        const code = (p.code || '').trim();
        if (!code || byCode.has(code)) return;
        byCode.set(code, {
            code: code,
            name: (p.name || '').trim()
        });
    });
    return Array.from(byCode.values()).sort((a, b) => a.code.localeCompare(b.code, 'ru'));
}

function formatProjectAutocompleteValue(entry) {
    if (!entry) return '';
    return entry.name ? (entry.code + ' - ' + entry.name) : entry.code;
}

function resolveProjectCodeFromInput(rawInput) {
    const input = (rawInput || '').trim();
    if (!input) return '';
    const entries = projectCodeEntries();

    const exactCode = entries.find(e => e.code.toLowerCase() === input.toLowerCase());
    if (exactCode) return exactCode.code;

    const exactDisplayMatches = entries.filter(e => formatProjectAutocompleteValue(e).toLowerCase() === input.toLowerCase());
    if (exactDisplayMatches.length === 1) return exactDisplayMatches[0].code;

    const byUniqueName = entries.filter(e => (e.name || '').toLowerCase() === input.toLowerCase());
    if (byUniqueName.length === 1) return byUniqueName[0].code;

    if (input.includes(' - ')) {
        const codeCandidate = input.split(' - ')[0].trim();
        const byCandidate = entries.find(e => e.code.toLowerCase() === codeCandidate.toLowerCase());
        if (byCandidate) return byCandidate.code;
    }

    return '';
}

function populateProjectAutocomplete() {
    const options = document.getElementById('config-action-project-options');
    options.innerHTML = '';
    projectCodeEntries().forEach(entry => {
        const opt = document.createElement('option');
        opt.value = formatProjectAutocompleteValue(entry);
        opt.label = entry.code;
        options.appendChild(opt);
    });
}

function variantsForProjectCode(projectCode) {
    const code = (projectCode || '').trim();
    if (!code) return [];
    return projectsCatalog
        .filter(p => (p.code || '').trim() === code)
        .map(p => ({uuid: p.uuid, variant: normalizeVariantLabel(p.variant || '')}))
        .sort((a, b) => a.variant.localeCompare(b.variant, 'ru'));
}

function populateVariantAutocomplete(projectCode, selectedVariantLabel) {
    const options = document.getElementById('config-action-variant-options');
    const input = document.getElementById('config-action-variant-input');
    const variants = variantsForProjectCode(projectCode);
    options.innerHTML = '';
    variants.forEach(v => {
        const opt = document.createElement('option');
        opt.value = v.variant;
        options.appendChild(opt);
    });
    if (selectedVariantLabel) {
        input.value = selectedVariantLabel;
    } else if (variants.length === 1) {
        input.value = variants[0].variant;
    } else {
        input.value = '';
    }
}

function resolveTargetProjectUUIDFromInputs() {
    const projectCode = resolveProjectCodeFromInput(document.getElementById('config-action-project-input').value);
    if (!projectCode) {
        return {error: 'Выберите проект из подсказок'};
    }
    const variantLabel = normalizeVariantLabel(document.getElementById('config-action-variant-input').value || 'main');
    const target = projectsCatalog.find(p =>
        (p.code || '').trim() === projectCode &&
        normalizeVariantLabel(p.variant || '') === variantLabel
    );
    if (!target) {
        return {error: 'Выберите вариант из подсказок'};
    }
    return {uuid: target.uuid};
}

function syncActionModalMode() {
    const copyCheckbox = document.getElementById('config-action-copy');
    if (copyCheckbox.checked) {
        // no-op: copy always uses latest revision
    } else {
        // no-op: copy always uses latest revision
    }
}

async function openConfigActionModal(uuid, currentName, currentProjectUUID) {
    document.getElementById('config-action-uuid').value = uuid;
    document.getElementById('config-action-current-name').value = currentName;
    document.getElementById('config-action-current-project').value = currentProjectUUID || projectUUID;
    document.getElementById('config-action-name').value = currentName;
    document.getElementById('config-action-copy').checked = false;
    populateProjectAutocomplete();
    const currentProject = projectsCatalog.find(p => p.uuid === (currentProjectUUID || projectUUID));
    if (currentProject) {
        const entry = {
            code: (currentProject.code || '').trim(),
            name: (currentProject.name || '').trim()
        };
        document.getElementById('config-action-project-input').value = formatProjectAutocompleteValue(entry);
        populateVariantAutocomplete(entry.code, normalizeVariantLabel(currentProject.variant || ''));
    } else {
        document.getElementById('config-action-project-input').value = '';
        populateVariantAutocomplete('', '');
    }
    syncActionModalMode();
    document.getElementById('config-action-modal').classList.remove('hidden');
    document.getElementById('config-action-modal').classList.add('flex');
    const nameInput = document.getElementById('config-action-name');
    nameInput.focus();
    nameInput.select();
}

function closeConfigActionModal() {
    document.getElementById('config-action-modal').classList.add('hidden');
    document.getElementById('config-action-modal').classList.remove('flex');
}

async function saveConfigAction() {
    const notify = (message, type) => {
        if (typeof showToast === 'function') {
            showToast(message, type || 'success');
        } else {
            alert(message);
        }
    };

    const uuid = document.getElementById('config-action-uuid').value;
    const currentName = document.getElementById('config-action-current-name').value;
    const currentProjectUUID = document.getElementById('config-action-current-project').value || projectUUID;
    const name = document.getElementById('config-action-name').value.trim();
    const copy = document.getElementById('config-action-copy').checked;
    const targetProject = resolveTargetProjectUUIDFromInputs();
    if (targetProject.error) {
        notify(targetProject.error, 'error');
        return;
    }
    const targetProjectUUID = targetProject.uuid || currentProjectUUID;

    if (!name) {
        notify('Введите название', 'error');
        return;
    }
    const uniqueNameResult = await resolveUniqueConfigActionName(name, targetProjectUUID, copy ? '' : uuid);
    if (uniqueNameResult.error) {
        notify(uniqueNameResult.error, 'error');
        return;
    }
    const resolvedName = uniqueNameResult.name;
    if (uniqueNameResult.changed) {
        document.getElementById('config-action-name').value = resolvedName;
        notify('Имя занято, использовано ' + resolvedName, 'success');
    }

    if (copy) {
        const cloneResp = await fetch('/api/projects/' + targetProjectUUID + '/configs/' + uuid + '/clone', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({name: resolvedName})
        });
        if (!cloneResp.ok) {
            notify('Не удалось скопировать конфигурацию', 'error');
            return;
        }
        closeConfigActionModal();
        await loadConfigs();
        notify('Копия создана', 'success');
        if (targetProjectUUID && targetProjectUUID !== projectUUID) {
            window.location.href = '/projects/' + targetProjectUUID;
            return;
        }
        return;
    }

    let changed = false;
    if (resolvedName !== currentName) {
        const renameResp = await fetch('/api/configs/' + uuid + '/rename', {
            method: 'PATCH',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({name: resolvedName})
        });
        if (!renameResp.ok) {
            notify('Не удалось переименовать конфигурацию', 'error');
            return;
        }
        changed = true;
    }

    if (targetProjectUUID !== currentProjectUUID) {
        const moveResp = await fetch('/api/configs/' + uuid + '/project', {
            method: 'PATCH',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({project_uuid: targetProjectUUID})
        });
        if (!moveResp.ok) {
            notify('Не удалось перенести конфигурацию', 'error');
            return;
        }
        changed = true;
    }

    if (!changed) {
        closeConfigActionModal();
        return;
    }

    closeConfigActionModal();
    await loadConfigs();
    notify('Изменения сохранены', 'success');
    if (targetProjectUUID && targetProjectUUID !== projectUUID) {
        window.location.href = '/projects/' + targetProjectUUID;
    }
}

function openProjectSettingsModal() {
    if (!project) return;
    if (project.is_system) {
        alert('Системный проект нельзя редактировать');
        return;
    }
    document.getElementById('project-settings-code').value = project.code || '';
    document.getElementById('project-settings-variant').value = project.variant || '';
    document.getElementById('project-settings-name').value = project.name || '';
    document.getElementById('project-settings-tracker-url').value = (project.tracker_url || '').trim();
    document.getElementById('project-settings-modal').classList.remove('hidden');
    document.getElementById('project-settings-modal').classList.add('flex');
}

function closeProjectSettingsModal() {
    document.getElementById('project-settings-modal').classList.add('hidden');
    document.getElementById('project-settings-modal').classList.remove('flex');
}

async function saveProjectSettings() {
    if (!project) return;
    const code = document.getElementById('project-settings-code').value.trim();
    const variant = document.getElementById('project-settings-variant').value.trim();
    const name = document.getElementById('project-settings-name').value.trim();
    const trackerURL = document.getElementById('project-settings-tracker-url').value.trim();
    if (!code) {
        alert('Введите код проекта');
        return;
    }
    const resp = await fetch('/api/projects/' + projectUUID, {
        method: 'PUT',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({code: code, variant: variant, name: name, tracker_url: trackerURL})
    });
    if (!resp.ok) {
        if (resp.status === 409) {
            alert('Проект с таким кодом и вариантом уже существует');
            return;
        }
        alert('Не удалось сохранить параметры проекта');
        return;
    }
    project = await resp.json();
    document.getElementById('project-code').textContent = project.code || '—';
    await loadVariantsForCode(project.code || '');
    renderVariantSelect();
    const trackerLink = document.getElementById('tracker-link');
    if (trackerLink) {
        const trackerURLResolved = resolveProjectTrackerURL(project);
        if (trackerURLResolved) {
            trackerLink.href = trackerURLResolved;
            trackerLink.classList.remove('hidden');
        } else {
            trackerLink.classList.add('hidden');
        }
    }
    closeProjectSettingsModal();
}

async function deleteVariant() {
    if (!project) return;
    const variantLabel = normalizeVariantLabel(project.variant);
    if (!confirm('Удалить вариант «' + variantLabel + '»? Все конфигурации будут архивированы.')) return;
    const resp = await fetch('/api/projects/' + projectUUID, {method: 'DELETE'});
    if (!resp.ok) {
        const data = await resp.json().catch(() => ({}));
        alert(data.error || 'Не удалось удалить вариант');
        return;
    }
    // Redirect to main variant or projects list
    const mainVariant = projectVariants.find(v => normalizeVariantLabel(v.variant) === 'main');
    if (mainVariant && mainVariant.uuid !== projectUUID) {
        window.location.href = '/projects/' + mainVariant.uuid;
    } else {
        window.location.href = '/projects';
    }
}

function updateDeleteVariantButton() {
    const btn = document.getElementById('delete-variant-btn');
    if (!btn || !project) return;
    if ((project.variant || '').trim() !== '') {
        btn.classList.remove('hidden');
    } else {
        btn.classList.add('hidden');
    }
}

document.getElementById('create-modal').addEventListener('click', function(e) { if (e.target === this) closeCreateModal(); });
document.getElementById('vendor-import-modal').addEventListener('click', function(e) { if (e.target === this) closeVendorImportModal(); });
document.getElementById('new-variant-modal').addEventListener('click', function(e) { if (e.target === this) closeNewVariantModal(); });
document.getElementById('variant-action-modal').addEventListener('click', function(e) { if (e.target === this) closeVariantActionModal(); });
document.getElementById('config-action-modal').addEventListener('click', function(e) { if (e.target === this) closeConfigActionModal(); });
document.getElementById('project-settings-modal').addEventListener('click', function(e) { if (e.target === this) closeProjectSettingsModal(); });
document.getElementById('config-action-project-input').addEventListener('input', function(e) {
    const code = resolveProjectCodeFromInput(e.target.value);
    populateVariantAutocomplete(code, '');
});
document.getElementById('config-action-copy').addEventListener('change', function(e) {
    const currentName = document.getElementById('config-action-current-name').value;
    const nameInput = document.getElementById('config-action-name');
    if (e.target.checked && nameInput.value.trim() === currentName.trim()) {
        nameInput.value = currentName + '_копия';
    }
    syncActionModalMode();
});
document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') {
        closeCreateModal();
        closeVendorImportModal();
        closeVariantActionModal();
        closeConfigActionModal();
        closeProjectSettingsModal();
    }
});

function onConfigDragStart(event) {
    if (configStatusMode !== 'active' || isReorderingConfigs) {
        event.preventDefault();
        return;
    }
    const row = event.target.closest('tr[data-config-uuid]');
    if (!row) {
        event.preventDefault();
        return;
    }

    dragConfigUUID = row.dataset.configUuid || '';
    if (!dragConfigUUID) {
        event.preventDefault();
        return;
    }
    row.classList.add('opacity-50');
    event.dataTransfer.effectAllowed = 'move';
    event.dataTransfer.setData('text/plain', dragConfigUUID);
}

function onConfigDragOver(event) {
    if (!dragConfigUUID || configStatusMode !== 'active') return;
    event.preventDefault();
    const row = event.target.closest('tr[data-config-uuid]');
    if (!row || row.dataset.configUuid === dragConfigUUID) return;
    row.classList.add('ring-2', 'ring-blue-300');
}

function onConfigDragLeave(event) {
    const row = event.target.closest('tr[data-config-uuid]');
    if (!row) return;
    row.classList.remove('ring-2', 'ring-blue-300');
}

async function onConfigDrop(event) {
    if (!dragConfigUUID || configStatusMode !== 'active' || isReorderingConfigs) return;
    event.preventDefault();

    const targetRow = event.target.closest('tr[data-config-uuid]');
    if (!targetRow) return;
    targetRow.classList.remove('ring-2', 'ring-blue-300');

    const targetUUID = targetRow.dataset.configUuid || '';
    if (!targetUUID || targetUUID === dragConfigUUID) return;

    const previous = allConfigs.slice();
    const fromIndex = allConfigs.findIndex(c => c.uuid === dragConfigUUID);
    const toIndex = allConfigs.findIndex(c => c.uuid === targetUUID);
    if (fromIndex < 0 || toIndex < 0) return;

    const [moved] = allConfigs.splice(fromIndex, 1);
    allConfigs.splice(toIndex, 0, moved);
    renderConfigs(allConfigs);
    await saveConfigReorder(previous);
}

function onConfigDragEnd() {
    document.querySelectorAll('tr[data-config-uuid]').forEach(row => {
        row.classList.remove('ring-2', 'ring-blue-300', 'opacity-50');
    });
    dragConfigUUID = '';
}

async function saveConfigReorder(previousConfigs) {
    if (isReorderingConfigs) return;
    isReorderingConfigs = true;
    const orderedUUIDs = allConfigs.map(c => c.uuid);

    try {
        const resp = await fetch('/api/projects/' + projectUUID + '/configs/reorder', {
            method: 'PATCH',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({ordered_uuids: orderedUUIDs}),
        });
        if (!resp.ok) {
            const data = await resp.json().catch(() => ({}));
            throw new Error(data.error || 'Не удалось сохранить порядок');
        }
        const data = await resp.json();
        allConfigs = data.configurations || allConfigs;
        renderConfigs(allConfigs);
        if (typeof showToast === 'function') {
            showToast('Порядок сохранён', 'success');
        }
    } catch (e) {
        allConfigs = previousConfigs.slice();
        renderConfigs(allConfigs);
        if (typeof showToast === 'function') {
            showToast(e.message || 'Не удалось сохранить порядок', 'error');
        } else {
            alert(e.message || 'Не удалось сохранить порядок');
        }
    } finally {
        isReorderingConfigs = false;
        dragConfigUUID = '';
    }
}

async function updateConfigServerCount(input) {
    const uuid = input.dataset.uuid;
    const prevValue = parseInt(input.dataset.prev) || 1;
    const newValue = parseInt(input.value);
    if (!newValue || newValue < 1) {
        input.value = prevValue;
        return;
    }
    try {
        const resp = await fetch('/api/configs/' + uuid + '/server-count', {
            method: 'PATCH',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({server_count: newValue})
        });
        if (!resp.ok) {
            input.value = prevValue;
            showToast('Не удалось обновить количество', 'error');
            return;
        }
        const updated = await resp.json();
        input.dataset.prev = newValue;
        // Update row total price
        const totalCell = document.querySelector('[data-total-uuid="' + uuid + '"]');
        if (totalCell && updated.total_price != null) {
            totalCell.textContent = formatMoneyNoDecimals(updated.total_price);
        }
        // Update the config in allConfigs and recalculate footer total
        for (let i = 0; i < allConfigs.length; i++) {
            if (allConfigs[i].uuid === uuid) {
                allConfigs[i].total_price = updated.total_price;
                allConfigs[i].server_count = newValue;
                break;
            }
        }
        updateFooterTotal();
    } catch (e) {
        input.value = prevValue;
        showToast('Ошибка сети', 'error');
    }
}

function updateFooterTotal() {
    let totalSum = 0;
    allConfigs.forEach(c => { totalSum += (c.total_price || 0); });
    const footer = document.querySelector('tfoot td[data-footer-total]');
    if (footer) {
        footer.textContent = formatMoneyNoDecimals(totalSum);
    }
}

function openExportModal() {
    const modal = document.getElementById('project-export-modal');
    if (!modal) return;
    setProjectExportStatus('', '');
    modal.classList.remove('hidden');
    modal.classList.add('flex');
}

function closeExportModal() {
    const modal = document.getElementById('project-export-modal');
    if (!modal) return;
    modal.classList.add('hidden');
    modal.classList.remove('flex');
}

function setProjectExportStatus(message, tone) {
    const status = document.getElementById('project-export-status');
    if (!status) return;
    if (!message) {
        status.className = 'hidden text-sm rounded border px-3 py-2';
        status.textContent = '';
        return;
    }
    const palette = tone === 'error'
        ? 'text-red-700 bg-red-50 border-red-200'
        : 'text-gray-700 bg-gray-50 border-gray-200';
    status.className = 'text-sm rounded border px-3 py-2 ' + palette;
    status.textContent = message;
}

async function exportProject() {
    const submitBtn = document.getElementById('project-export-submit');
    const payload = {
        include_lot: !!document.getElementById('export-col-lot')?.checked,
        include_bom: !!document.getElementById('export-col-bom')?.checked,
        include_estimate: !!document.getElementById('export-col-estimate')?.checked,
        include_stock: !!document.getElementById('export-col-stock')?.checked,
        include_competitor: !!document.getElementById('export-col-competitor')?.checked,
        basis: document.querySelector('input[name="export-basis"]:checked')?.value || 'fob'
    };

    if (submitBtn) submitBtn.disabled = true;
    setProjectExportStatus('', '');

    try {
        const resp = await fetch('/api/projects/' + projectUUID + '/export', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(payload)
        });
        if (!resp.ok) {
            let message = 'Не удалось экспортировать CSV';
            try {
                const data = await resp.json();
                if (data && data.error) message = data.error;
            } catch (_) {}
            setProjectExportStatus(message, 'error');
            return;
        }

        const blob = await resp.blob();
        const link = document.createElement('a');
        const url = window.URL.createObjectURL(blob);
        const disposition = resp.headers.get('Content-Disposition') || '';
        const match = disposition.match(/filename="([^"]+)"/);
        link.href = url;
        link.download = match ? match[1] : 'project-export.csv';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        window.URL.revokeObjectURL(url);
        closeExportModal();
    } catch (e) {
        setProjectExportStatus('Ошибка сети при экспорте CSV', 'error');
    } finally {
        if (submitBtn) submitBtn.disabled = false;
    }
}

async function refreshPrices() {
    const configs = (allConfigs || []).filter(c => c.is_active !== false);
    if (!configs.length) {
        showToast('Нет активных конфигураций', 'error');
        return;
    }

    const btn = document.getElementById('refresh-all-prices-btn');
    if (btn) {
        btn.disabled = true;
        btn.textContent = 'Обновление...';
        btn.className = 'py-2 bg-gray-300 text-gray-500 rounded-lg cursor-not-allowed font-medium';
    }

    try {
        const latestEstimatePricelistId = await fetchLatestEstimatePricelistId();

        const diffResults = [];
        for (const cfg of configs) {
            if (cfg.disable_price_refresh) {
                diffResults.push({ configName: cfg.name, skipped: true });
                continue;
            }

            const prevTotal = cfg.total_price || 0;
            const prevItemsMap = {};
            if (cfg.items) {
                for (const item of cfg.items) prevItemsMap[item.lot_name] = item.unit_price;
            }

            try {
                const body = latestEstimatePricelistId ? JSON.stringify({ pricelist_id: latestEstimatePricelistId }) : undefined;
                const resp = await fetch('/api/configs/' + cfg.uuid + '/refresh-prices', {
                    method: 'POST',
                    headers: body ? { 'Content-Type': 'application/json' } : {},
                    body,
                });
                if (!resp.ok) {
                    diffResults.push({ configName: cfg.name, error: true });
                    continue;
                }
                const updated = await resp.json();

                cfg.total_price = updated.total_price ?? cfg.total_price;
                if (updated.items) cfg.items = updated.items;

                const totalCell = document.querySelector('[data-total-uuid="' + cfg.uuid + '"]');
                if (totalCell && updated.total_price != null) {
                    totalCell.textContent = formatMoneyNoDecimals(updated.total_price);
                    const sc = cfg.server_count || 1;
                    const row = totalCell.closest('tr');
                    if (row) {
                        const cells = row.querySelectorAll('td');
                        if (cells[4]) cells[4].textContent = formatMoneyNoDecimals(sc > 0 ? updated.total_price / sc : 0);
                    }
                }

                const itemDiffs = [];
                if (updated.items) {
                    for (const item of updated.items) {
                        const prevPrice = prevItemsMap[item.lot_name];
                        if (prevPrice !== undefined && Math.abs(prevPrice - item.unit_price) > 0.01) {
                            itemDiffs.push({ lot_name: item.lot_name, quantity: item.quantity, prevPrice, newPrice: item.unit_price });
                        }
                    }
                }

                diffResults.push({ configName: cfg.name, prevTotal, newTotal: updated.total_price || 0, serverCount: cfg.server_count || 1, itemDiffs });
            } catch(_) {
                diffResults.push({ configName: cfg.name, error: true });
            }
        }

        updateFooterTotal();
        showToast('Цены обновлены', 'success');
        showPriceDiffModal(diffResults);
    } catch(e) {
        showToast('Ошибка обновления цен', 'error');
    } finally {
        if (btn) {
            btn.disabled = false;
            btn.textContent = 'Обновить цены';
            btn.className = 'py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium';
        }
    }
}

document.addEventListener('DOMContentLoaded', async function() {
    applyStatusModeUI();
    const ok = await loadProject();
    if (!ok) return;
    updateDeleteVariantButton();
    await loadConfigs();
});
</script>
{{end}}

{{template "base" .}}
