{{define "title"}}QuoteForge - Конфигуратор{{end}}

{{define "content"}}
<div class="space-y-4">
    <!-- Header with config name and back button -->
    <div class="flex items-center justify-between">
        <div class="flex items-center space-x-4">
            <a href="/projects" class="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="text-2xl font-bold flex items-center gap-2" id="config-breadcrumbs">
                <a id="breadcrumb-project-code-link" href="/projects" class="text-blue-700 hover:underline">
                    <span id="breadcrumb-project-code">—</span>
                </a>
                <span class="text-gray-400">-</span>
                <a id="breadcrumb-project-variant-link" href="/projects" class="text-blue-700 hover:underline">
                    <span id="breadcrumb-project-variant">main</span>
                </a>
                <span class="text-gray-400">-</span>
                <a id="breadcrumb-config-name-link" href="#" class="text-blue-700 hover:underline">
                    <span id="breadcrumb-config-name">Конфигуратор</span>
                </a>
                <span class="text-gray-400">-</span>
                <span id="breadcrumb-config-version">v1</span>
            </div>
        </div>
        <div id="save-buttons" class="hidden flex items-center space-x-2">
            <button id="refresh-prices-btn" onclick="refreshPrices()" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
                Обновить цены
            </button>
            <button type="button"
                    onclick="openPriceSettingsModal()"
                    class="h-10 px-3 bg-gray-100 text-gray-700 rounded hover:bg-gray-200 border border-gray-300 inline-flex items-center justify-center"
                    title="Настройки цен">
                Цены
            </button>
            <button onclick="saveConfig()" class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">
                Сохранить
            </button>
            <span id="price-update-date" class="text-sm text-gray-500"></span>
        </div>
    </div>

    <!-- Server count input -->
    <div class="bg-white rounded-lg shadow p-4 mb-4">
        <div class="flex items-center space-x-4">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Количество серверов</label>
                <input type="number" id="server-count" min="1" value="1"
                       class="w-20 px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"
                       onchange="updateServerCount()">
            </div>
            <div class="text-sm text-gray-600">
                <div class="font-medium text-gray-700 mb-1">Прайслисты цен</div>
                <div id="pricelist-settings-summary">Estimate: авто, Склад: авто, Конкуренты: авто</div>
            </div>
            <div class="text-sm text-gray-500">
                <span id="server-count-info">Всего: <span id="total-server-count">1</span> сервер(а)</span>
            </div>
        </div>
    </div>

    <!-- Category Tabs -->
    <div class="bg-white rounded-lg shadow">
        <div class="border-b">
            <nav class="flex overflow-x-auto" id="category-tabs">
                <button onclick="switchTab('base')" data-tab="base"
                        class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-blue-600 text-blue-600 whitespace-nowrap">
                    Base
                </button>
                <button onclick="switchTab('storage')" data-tab="storage"
                        class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 whitespace-nowrap">
                    Storage
                </button>
                <button onclick="switchTab('pci')" data-tab="pci"
                        class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 whitespace-nowrap">
                    PCI
                </button>
                <button onclick="switchTab('power')" data-tab="power"
                        class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 whitespace-nowrap">
                    Power
                </button>
                <button onclick="switchTab('accessories')" data-tab="accessories"
                        class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 whitespace-nowrap">
                    Accessories
                </button>
                <button onclick="switchTab('other')" data-tab="other"
                        class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 whitespace-nowrap">
                    Other
                </button>
            </nav>
        </div>

        <!-- Tab content -->
        <div id="tab-content" class="p-4">
            <div class="text-center py-8 text-gray-500">Загрузка...</div>
        </div>
    </div>

    <!-- Cart summary -->
    <div id="cart-summary" class="bg-white rounded-lg shadow overflow-hidden">
        <button type="button"
                onclick="toggleCartSummarySection()"
                class="w-full px-4 py-3 flex items-center justify-between text-blue-900 bg-gradient-to-r from-blue-100 to-blue-50 hover:from-blue-200 hover:to-blue-100 border-b border-blue-200">
            <span class="font-semibold">Итого конфигурация</span>
            <svg id="cart-summary-toggle-icon" class="w-5 h-5 transition-transform duration-200" 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="cart-summary-content" class="p-4">
            <div id="article-display" class="text-sm text-gray-700 mb-3 font-mono"></div>
            <div id="cart-items" class="space-y-2 mb-4"></div>
            <div class="border-t pt-3 flex justify-between items-center">
                <div class="text-lg font-bold">
                    Итого: <span id="cart-total">$0.00</span>
                </div>
                <button onclick="exportCSV()" class="px-3 py-1 bg-gray-200 text-gray-700 rounded text-sm hover:bg-gray-300">Экспорт CSV</button>
            </div>
        </div>
    </div>

    <!-- Custom price section -->
    <div id="custom-price-section" class="bg-white rounded-lg shadow overflow-hidden">
        <button type="button"
                onclick="toggleCustomPriceSection()"
                class="w-full px-4 py-3 flex items-center justify-between text-blue-900 bg-gradient-to-r from-blue-100 to-blue-50 hover:from-blue-200 hover:to-blue-100 border-b border-blue-200">
            <span class="font-semibold">Своя цена</span>
            <svg id="custom-price-toggle-icon" class="w-5 h-5 transition-transform duration-200" 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="custom-price-content" class="p-4">
        <div class="flex items-center gap-4 mb-4">
            <div class="flex-1">
                <label class="block text-sm text-gray-600 mb-1">Введите целевую цену</label>
                <div class="flex items-center gap-2">
                    <span class="text-gray-500">$</span>
                    <input type="number" id="custom-price-input" step="0.01" min="0"
                           placeholder="0.00"
                           class="flex-1 px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"
                           oninput="calculateCustomPrice(); triggerAutoSave();">
                    <button onclick="clearCustomPrice()" class="px-3 py-2 text-gray-500 hover:text-gray-700">
                        <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="M6 18L18 6M6 6l12 12"></path>
                        </svg>
                    </button>
                </div>
            </div>
            <div id="discount-info" class="text-right hidden">
                <div class="text-sm text-gray-600">Скидка</div>
                <div class="text-2xl font-bold text-green-600" id="discount-percent">0%</div>
            </div>
        </div>

        <!-- Adjusted prices table -->
        <div id="adjusted-prices" class="hidden">
            <div class="border-t pt-3">
                <h4 class="text-sm font-medium text-gray-700 mb-2">Скорректированные цены</h4>
                <div class="overflow-x-auto">
                    <table class="w-full text-sm">
                        <thead class="bg-gray-50">
                            <tr>
                                <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">Компонент</th>
                                <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase">Кол-во</th>
                                <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase">Было</th>
                                <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase">Стало</th>
                                <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase">Итого</th>
                            </tr>
                        </thead>
                        <tbody id="adjusted-prices-body" class="divide-y"></tbody>
                        <tfoot class="bg-gray-50 font-medium">
                            <tr>
                                <td class="px-3 py-2" colspan="2">Итого</td>
                                <td class="px-3 py-2 text-right" id="adjusted-total-original">$0.00</td>
                                <td class="px-3 py-2 text-right text-green-600" id="adjusted-total-new">$0.00</td>
                                <td class="px-3 py-2 text-right text-green-600" id="adjusted-total-final">$0.00</td>
                            </tr>
                        </tfoot>
                    </table>
                </div>
            </div>
            <div class="mt-3 flex justify-end">
                <button onclick="exportCSVWithCustomPrice()" class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700">
                    Экспорт CSV со скидкой
                </button>
            </div>
        </div>
        </div>
    </div>

    <!-- Sale price section -->
    <div id="sale-price-section" class="bg-white rounded-lg shadow overflow-hidden">
        <button type="button"
                onclick="toggleSalePriceSection()"
                class="w-full px-4 py-3 flex items-center justify-between text-blue-900 bg-gradient-to-r from-blue-100 to-blue-50 hover:from-blue-200 hover:to-blue-100 border-b border-blue-200">
            <span class="font-semibold">Цена продажи</span>
            <svg id="sale-price-toggle-icon" class="w-5 h-5 transition-transform duration-200" 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="sale-price-content" class="p-4">
        <div id="sale-prices" class="border-t pt-3">
            <div class="overflow-x-auto">
                <table class="w-full text-sm">
                    <thead class="bg-gray-50">
                        <tr>
                            <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">Артикул</th>
                            <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase">Кол-во</th>
                            <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase">Est. Price</th>
                            <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase">Склад</th>
                            <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase">Конкуренты</th>
                        </tr>
                    </thead>
                    <tbody id="sale-prices-body" class="divide-y"></tbody>
                    <tfoot class="bg-gray-50 font-medium">
                        <tr>
                            <td class="px-3 py-2">Итого</td>
                            <td class="px-3 py-2 text-right">—</td>
                            <td class="px-3 py-2 text-right" id="sale-total-est">$0.00</td>
                            <td class="px-3 py-2 text-right" id="sale-total-warehouse">$0.00</td>
                            <td class="px-3 py-2 text-right" id="sale-total-competitor">$0.00</td>
                        </tr>
                    </tfoot>
                </table>
            </div>
        </div>
        </div>
    </div>
</div>

<!-- Price settings modal -->
<div id="price-settings-modal" class="hidden fixed inset-0 z-50">
    <div class="absolute inset-0 bg-black/40" onclick="closePriceSettingsModal()"></div>
    <div class="relative max-w-xl mx-auto mt-24 bg-white rounded-lg shadow-xl border">
        <div class="px-5 py-4 border-b flex items-center justify-between">
            <h3 class="text-lg font-semibold text-gray-900">Настройки цен</h3>
            <button type="button" onclick="closePriceSettingsModal()" class="text-gray-500 hover:text-gray-700">
                <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="M6 18L18 6M6 6l12 12"></path>
                </svg>
            </button>
        </div>
        <div class="px-5 py-4 space-y-4">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Estimate</label>
                <select id="settings-pricelist-estimate" class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"></select>
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Склад</label>
                <select id="settings-pricelist-warehouse" class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"></select>
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Конкуренты</label>
                <select id="settings-pricelist-competitor" class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"></select>
            </div>
            <label class="flex items-center gap-2 text-sm text-gray-700">
                <input id="settings-disable-price-refresh" type="checkbox" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
                <span>Не обновлять цены</span>
            </label>
            <label class="flex items-center gap-2 text-sm text-gray-700">
                <input id="settings-only-in-stock" type="checkbox" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
                <span>Только наличие</span>
            </label>
        </div>
        <div class="px-5 py-4 border-t flex justify-end gap-2">
            <button type="button" onclick="closePriceSettingsModal()" class="px-4 py-2 bg-gray-100 text-gray-700 rounded hover:bg-gray-200">Отмена</button>
            <button type="button" onclick="applyPriceSettings()" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Применить</button>
        </div>
    </div>
</div>

<!-- Autocomplete dropdown (shared) -->
<div id="autocomplete-dropdown" class="hidden absolute z-50 bg-white border rounded-lg shadow-lg max-h-96 overflow-y-auto w-96"></div>

<style>
.autocomplete-item {
    padding: 8px 12px;
    cursor: pointer;
    border-bottom: 1px solid #f0f0f0;
}
.autocomplete-item:hover, .autocomplete-item.selected {
    background-color: #f3f4f6;
}
.autocomplete-item:last-child {
    border-bottom: none;
}
</style>

<script>
// Tab configuration - will be populated dynamically
let TAB_CONFIG = {
    base: {
        categories: ['MB', 'CPU', 'MEM'],
        singleSelect: true,
        label: 'Base'
    },
    storage: {
        categories: ['RAID', 'M2', 'SSD', 'HDD', 'EDSFF', 'HHHL'],
        singleSelect: false,
        label: 'Storage',
        sections: [
            { title: 'RAID Контроллеры', categories: ['RAID'] },
            { title: 'Диски', categories: ['M2', 'SSD', 'HDD', 'EDSFF', 'HHHL'] }
        ]
    },
    pci: {
        categories: ['GPU', 'DPU', 'NIC', 'HCA', 'HBA'],
        singleSelect: false,
        label: 'PCI',
        sections: [
            { title: 'GPU / DPU', categories: ['GPU', 'DPU'] },
            { title: 'NIC / HCA', categories: ['NIC', 'HCA'] },
            { title: 'HBA', categories: ['HBA'] }
        ]
    },
    power: {
        categories: ['PS', 'PSU'],
        singleSelect: false,
        label: 'Power'
    },
    accessories: {
        categories: ['ACC', 'CARD'],
        singleSelect: false,
        label: 'Accessories'
    },
    other: {
        categories: [],
        singleSelect: false,
        label: 'Other'
    }
};

let ASSIGNED_CATEGORIES = Object.values(TAB_CONFIG)
    .flatMap(t => t.categories)
    .map(c => c.toUpperCase());

// State
let configUUID = '{{.ConfigUUID}}';
let configName = '';
let currentVersionNo = 1;
let projectUUID = '';
let projectName = '';
let projectCode = '';
let projectVariant = '';
let projectIndexLoaded = false;
let projectByUUID = {};
let projectMainByCode = {};

async function loadProjectIndex() {
    if (projectIndexLoaded) return;
    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 || []);
        projectByUUID = {};
        projectMainByCode = {};
        allProjects.forEach(p => {
            projectByUUID[p.uuid] = p;
            const code = (p.code || '').trim();
            const variant = (p.variant || '').trim();
            if (code && (variant === '' || variant === 'main')) {
                if (!projectMainByCode[code]) {
                    projectMainByCode[code] = p.uuid;
                }
            }
        });
        projectIndexLoaded = true;
    } catch (e) {
        // ignore
    }
}

function updateConfigBreadcrumbs() {
    const codeEl = document.getElementById('breadcrumb-project-code');
    const variantEl = document.getElementById('breadcrumb-project-variant');
    const configEl = document.getElementById('breadcrumb-config-name');
    const versionEl = document.getElementById('breadcrumb-config-version');
    const projectCodeLinkEl = document.getElementById('breadcrumb-project-code-link');
    const projectVariantLinkEl = document.getElementById('breadcrumb-project-variant-link');

    let code = 'Без проекта';
    let variant = 'main';
    if (projectUUID && projectByUUID[projectUUID]) {
        code = (projectByUUID[projectUUID].code || '').trim() || 'Без проекта';
        const rawVariant = (projectByUUID[projectUUID].variant || '').trim();
        variant = rawVariant === '' ? 'main' : rawVariant;
        if (projectCodeLinkEl) {
            const mainUUID = projectMainByCode[code];
            projectCodeLinkEl.href = mainUUID ? ('/projects/' + mainUUID) : ('/projects/' + projectUUID);
        }
        if (projectVariantLinkEl) {
            projectVariantLinkEl.href = '/projects/' + projectUUID;
        }
    } else {
        if (projectCodeLinkEl) projectCodeLinkEl.href = '/projects';
        if (projectVariantLinkEl) projectVariantLinkEl.href = '/projects';
    }
    codeEl.textContent = code;
    variantEl.textContent = variant;
    const fullConfigName = configName || 'Конфигурация';
    configEl.textContent = truncateBreadcrumbSpecName(fullConfigName);
    configEl.title = fullConfigName;
    versionEl.textContent = 'v' + (currentVersionNo || 1);
    const configNameLinkEl = document.getElementById('breadcrumb-config-name-link');
    if (configNameLinkEl && configUUID) {
        configNameLinkEl.href = '/configs/' + configUUID + '/revisions';
    }
}

function truncateBreadcrumbSpecName(name) {
    const maxLength = 16;
    if (!name || name.length <= maxLength) return name;
    return name.slice(0, maxLength - 1) + '…';
}
let currentTab = 'base';
let allComponents = [];
let cart = [];
let categoryOrderMap = {}; // Category code -> display_order mapping
let autoSaveTimeout = null; // Timeout for debounced autosave
let hasUnsavedChanges = false;
let exitSaveStarted = false;
let serverCount = 1; // Server count for the configuration
let serverModelForQuote = '';
let supportCode = '';
let currentArticle = '';
let articlePreviewTimeout = null;
let selectedPricelistIds = {
    estimate: null,
    warehouse: null,
    competitor: null
};
let resolvedAutoPricelistIds = {
    estimate: null,
    warehouse: null,
    competitor: null
};
let disablePriceRefresh = false;
let onlyInStock = false;
let activePricelistsBySource = {
    estimate: [],
    warehouse: [],
    competitor: []
};
let activePricelistsLoadedAt = 0;
let activePricelistsLoadPromise = null;
let priceLevelsRequestSeq = 0;
let priceLevelsRefreshTimer = null;
let warehouseStockLotsByPricelist = new Map();
let warehouseStockLoadSeq = 0;
let warehouseStockLoadsByPricelist = new Map();
let componentPricesCache = {}; // { lot_name: price } - caches prices loaded via API
let componentPricesCacheLoading = new Map(); // { category: Promise } - tracks ongoing price loads

// Autocomplete state
let autocompleteInput = null;
let autocompleteCategory = null;
let autocompleteMode = null; // 'single', 'multi', 'section'
let autocompleteIndex = -1;
let autocompleteFiltered = [];

function getDisplayPrice(item) {
    if (typeof item.unit_price === 'number' && item.unit_price > 0) {
        return item.unit_price;
    }
    if (typeof item.estimate_price === 'number' && item.estimate_price > 0) {
        return item.estimate_price;
    }
    return 0;
}

function formatNumberRu(value) {
    const rounded = Math.round(value);
    return rounded
        .toLocaleString('ru-RU', { minimumFractionDigits: 0, maximumFractionDigits: 0 })
        .replace(/[\u202f\u00a0 ]/g, '\u00A0');
}

function formatMoney(value) {
    return '$\u00A0' + formatNumberRu(value);
}

function formatPriceOrNA(value) {
    if (typeof value !== 'number' || value <= 0) {
        return 'N/A';
    }
    return formatMoney(value);
}

function formatDelta(abs, pct) {
    if (typeof abs !== 'number') {
        return 'N/A';
    }
    const sign = abs > 0 ? '+' : abs < 0 ? '-' : '';
    const absValue = Math.abs(abs);
    if (typeof pct !== 'number') {
        return sign + formatMoney(absValue);
    }
    const pctSign = pct > 0 ? '+' : pct < 0 ? '-' : '';
    return sign + formatMoney(absValue) + ' (' + pctSign + Math.round(Math.abs(pct)) + '%)';
}

function getEffectivePricelistID(source) {
    const explicit = selectedPricelistIds[source];
    if (Number.isFinite(explicit) && explicit > 0) {
        return Number(explicit);
    }
    const resolvedAuto = resolvedAutoPricelistIds[source];
    if (Number.isFinite(resolvedAuto) && resolvedAuto > 0) {
        return Number(resolvedAuto);
    }
    const fallback = activePricelistsBySource[source]?.[0]?.id;
    if (Number.isFinite(fallback) && fallback > 0) {
        return Number(fallback);
    }
    return null;
}

async function refreshPriceLevels(options = {}) {
    const force = options.force === true;
    const noCache = options.noCache === true;
    if (!configUUID || cart.length === 0 || (disablePriceRefresh && !force)) {
        return;
    }

    const seq = ++priceLevelsRequestSeq;
    try {
        const payload = {
            items: cart.map(item => ({
                lot_name: item.lot_name,
                quantity: item.quantity
            })),
            no_cache: noCache,
            pricelist_ids: Object.fromEntries(
                Object.entries(selectedPricelistIds)
                    .filter(([, id]) => typeof id === 'number' && id > 0)
            )
        };
        const resp = await fetch('/api/quote/price-levels', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload)
        });
        if (!resp.ok) {
            return;
        }
        const data = await resp.json();
        if (seq !== priceLevelsRequestSeq) {
            return;
        }
        const byLot = new Map((data.items || []).map(i => [i.lot_name, i]));
        cart = cart.map(item => {
            const levels = byLot.get(item.lot_name);
            if (!levels) return item;
            const next = { ...item, ...levels };
            if (typeof levels.estimate_price === 'number' && levels.estimate_price > 0) {
                next.unit_price = levels.estimate_price;
            }
            return next;
        });
        if (data.resolved_pricelist_ids) {
            ['estimate', 'warehouse', 'competitor'].forEach(source => {
                if (!selectedPricelistIds[source] && data.resolved_pricelist_ids[source]) {
                    resolvedAutoPricelistIds[source] = Number(data.resolved_pricelist_ids[source]);
                }
            });
            renderPricelistSettingsSummary();
        }
    } catch(e) {
        console.error('Failed to refresh price levels', e);
    }
}

function schedulePriceLevelsRefresh(options = {}) {
    const delay = Number.isFinite(options.delay) ? options.delay : 120;
    const rerender = options.rerender !== false;
    const autosave = options.autosave === true;
    const noCache = options.noCache === true;
    const force = options.force === true;

    if (priceLevelsRefreshTimer) {
        clearTimeout(priceLevelsRefreshTimer);
        priceLevelsRefreshTimer = null;
    }

    priceLevelsRefreshTimer = setTimeout(async () => {
        priceLevelsRefreshTimer = null;
        await refreshPriceLevels({ noCache, force });
        if (rerender) {
            renderTab();
            updateCartUI();
        }
        if (autosave) {
            triggerAutoSave();
        }
    }, Math.max(0, delay));
}

function currentWarehousePricelistID() {
    return getEffectivePricelistID('warehouse');
}

async function loadWarehouseInStockLots() {
    const pricelistID = currentWarehousePricelistID();
    if (!pricelistID) return new Set();
    if (warehouseStockLotsByPricelist.has(pricelistID)) {
        return warehouseStockLotsByPricelist.get(pricelistID);
    }
    const existingLoad = warehouseStockLoadsByPricelist.get(pricelistID);
    if (existingLoad) {
        return existingLoad;
    }

    const loadPromise = (async () => {
        const seq = ++warehouseStockLoadSeq;
        const result = new Set();
        const resp = await fetch(`/api/pricelists/${pricelistID}/lots`);
        if (!resp.ok) {
            throw new Error(`warehouse lots request failed: ${resp.status}`);
        }
        const data = await resp.json();
        const lotNames = Array.isArray(data.lot_names) ? data.lot_names : [];
        lotNames.forEach(lot => {
            if (typeof lot === 'string' && lot.trim() !== '') {
                result.add(lot);
            }
        });

        if (seq === warehouseStockLoadSeq) {
            warehouseStockLotsByPricelist.set(pricelistID, result);
        }
        return result;
    })();

    warehouseStockLoadsByPricelist.set(pricelistID, loadPromise);
    try {
        return await loadPromise;
    } finally {
        warehouseStockLoadsByPricelist.delete(pricelistID);
    }
}

async function ensureWarehouseStockFilterLoaded() {
    if (!onlyInStock) return;
    try {
        await loadWarehouseInStockLots();
    } catch (e) {
        console.error('Failed to load warehouse availability filter', e);
        showToast('Не удалось загрузить наличие склада', 'error');
    }
}

function isComponentAllowedByStockFilter(comp) {
    if (!onlyInStock) return true;
    const pricelistID = currentWarehousePricelistID();
    if (!pricelistID) return false;
    const availableLots = warehouseStockLotsByPricelist.get(pricelistID);
    // Don't block UI while stock set is being loaded.
    if (!availableLots) return true;
    return availableLots.has(comp.lot_name);
}

// Load categories from API and update tab configuration
async function loadCategoriesFromAPI() {
    try {
        const resp = await fetch('/api/categories');
        const cats = await resp.json();

        // Build category order map
        categoryOrderMap = {};
        cats.forEach(cat => {
            categoryOrderMap[cat.code.toUpperCase()] = cat.display_order;
        });

        // Build list of unassigned categories
        const knownCodes = Object.values(TAB_CONFIG)
            .flatMap(t => t.categories)
            .map(c => c.toUpperCase());

        const unassignedCategories = cats
            .filter(cat => !knownCodes.includes(cat.code.toUpperCase()))
            .sort((a, b) => a.display_order - b.display_order)
            .map(cat => cat.code);

        // Update "other" tab with unassigned categories
        TAB_CONFIG.other.categories = unassignedCategories;

        // Rebuild ASSIGNED_CATEGORIES
        ASSIGNED_CATEGORIES = Object.values(TAB_CONFIG)
            .flatMap(t => t.categories)
            .map(c => c.toUpperCase());
    } catch(e) {
        console.error('Failed to load categories, using defaults', e);
        // Will use default configuration if API fails
    }
}

// Initialize
document.addEventListener('DOMContentLoaded', async function() {
    // RBAC disabled - no token check required
    if (!configUUID) {
        window.location.href = '/configs';
        return;
    }

    // Load categories in background (defaults are usable immediately).
    const categoriesPromise = loadCategoriesFromAPI().catch(() => {});

    try {
        const resp = await fetch('/api/configs/' + configUUID);

        if (resp.status === 404) {
            showToast('Конфигурация не найдена', 'error');
            window.location.href = '/configs';
            return;
        }

        const config = await resp.json();
        configName = config.name;
        currentVersionNo = config.current_version_no || 1;
        projectUUID = config.project_uuid || '';
        await loadProjectIndex();
        updateConfigBreadcrumbs();
        document.getElementById('save-buttons').classList.remove('hidden');

        // Set server count from config
        serverCount = config.server_count || 1;
        document.getElementById('server-count').value = serverCount;
        document.getElementById('total-server-count').textContent = serverCount;
        selectedPricelistIds.estimate = config.pricelist_id || null;
        onlyInStock = Boolean(config.only_in_stock);

        if (config.items && config.items.length > 0) {
            cart = config.items.map(item => ({
                lot_name: item.lot_name,
                quantity: item.quantity,
                unit_price: item.unit_price,
                estimate_price: item.unit_price,
                warehouse_price: null,
                competitor_price: null,
                description: item.description || '',
                category: item.category || getCategoryFromLotName(item.lot_name)
            }));
        }
        serverModelForQuote = config.server_model || '';
        supportCode = config.support_code || '';
        currentArticle = config.article || '';

        // Restore custom price if saved
        if (config.custom_price) {
            document.getElementById('custom-price-input').value = config.custom_price.toFixed(2);
        }

        // Display price update date if available
        if (config.price_updated_at) {
            updatePriceUpdateDate(config.price_updated_at);
        }
    } catch(e) {
        showToast('Ошибка загрузки конфигурации', 'error');
        window.location.href = '/configs';
        return;
    }

    restoreLocalPriceSettings();
    await Promise.all([
        loadActivePricelists(),
        loadAllComponents(),
        categoriesPromise,
    ]);
    syncPriceSettingsControls();
    renderPricelistSettingsSummary();
    updateRefreshPricesButtonState();
    renderTab();
    updateCartUI();
    ensureWarehouseStockFilterLoaded().then(() => {
        renderTab();
        updateCartUI();
    });
    schedulePriceLevelsRefresh({ delay: 0, rerender: true, autosave: false });

    // Close autocomplete on outside click
    document.addEventListener('click', function(e) {
        if (!e.target.closest('.autocomplete-wrapper')) {
            hideAutocomplete();
        }
    });
});

async function loadAllComponents() {
    try {
        const resp = await fetch('/api/components?per_page=5000');
        const data = await resp.json();
        allComponents = data.components || [];
    } catch(e) {
        console.error('Failed to load components', e);
        allComponents = [];
    }
}

function updateServerCount() {
    const serverCountInput = document.getElementById('server-count');
    const newCount = parseInt(serverCountInput.value) || 1;
    serverCount = Math.max(1, newCount);
    serverCountInput.value = serverCount;

    // Update total server count display
    document.getElementById('total-server-count').textContent = serverCount;

    // Update cart UI to reflect the server count
    updateCartUI();

    // Trigger auto-save
    triggerAutoSave();
}

async function loadActivePricelists(force = false) {
    const now = Date.now();
    const isFresh = (now - activePricelistsLoadedAt) < 15000;
    if (!force && isFresh) {
        return;
    }
    if (activePricelistsLoadPromise) {
        await activePricelistsLoadPromise;
        return;
    }

    const sources = ['estimate', 'warehouse', 'competitor'];
    activePricelistsLoadPromise = (async () => {
        await Promise.all(sources.map(async source => {
            try {
                const resp = await fetch(`/api/pricelists?active_only=true&source=${source}&per_page=200`);
                const data = await resp.json();
                activePricelistsBySource[source] = data.pricelists || [];
                const existing = selectedPricelistIds[source];
                if (existing && activePricelistsBySource[source].some(pl => Number(pl.id) === Number(existing))) {
                    return;
                }
                selectedPricelistIds[source] = null;
            } catch (e) {
                activePricelistsBySource[source] = [];
                selectedPricelistIds[source] = null;
            }
        }));
        activePricelistsLoadedAt = Date.now();
    })();

    try {
        await activePricelistsLoadPromise;
    } finally {
        activePricelistsLoadPromise = null;
    }
}

function renderPricelistSelectOptions(selectId, source) {
    const select = document.getElementById(selectId);
    if (!select) return;
    const pricelists = activePricelistsBySource[source] || [];
    if (pricelists.length === 0) {
        select.innerHTML = '<option value="">Нет активных прайслистов</option>';
        select.value = '';
        return;
    }
    select.innerHTML = `<option value="">Авто (последний активный)</option>` + pricelists.map(pl => {
        return `<option value="${pl.id}">${escapeHtml(pl.version)}</option>`;
    }).join('');
    const current = selectedPricelistIds[source];
    select.value = current ? String(current) : '';
}

function syncPriceSettingsControls() {
    renderPricelistSelectOptions('settings-pricelist-estimate', 'estimate');
    renderPricelistSelectOptions('settings-pricelist-warehouse', 'warehouse');
    renderPricelistSelectOptions('settings-pricelist-competitor', 'competitor');
    const disableCheckbox = document.getElementById('settings-disable-price-refresh');
    if (disableCheckbox) {
        disableCheckbox.checked = disablePriceRefresh;
    }
    const inStockCheckbox = document.getElementById('settings-only-in-stock');
    if (inStockCheckbox) {
        inStockCheckbox.checked = onlyInStock;
    }
}

function getPricelistVersionById(source, id) {
    const pricelists = activePricelistsBySource[source] || [];
    const found = pricelists.find(pl => Number(pl.id) === Number(id));
    return found ? found.version : null;
}

function renderPricelistSettingsSummary() {
    const summary = document.getElementById('pricelist-settings-summary');
    if (!summary) return;
    const estimate = selectedPricelistIds.estimate ? getPricelistVersionById('estimate', selectedPricelistIds.estimate) || `ID ${selectedPricelistIds.estimate}` : 'авто';
    const warehouse = selectedPricelistIds.warehouse ? getPricelistVersionById('warehouse', selectedPricelistIds.warehouse) || `ID ${selectedPricelistIds.warehouse}` : 'авто';
    const competitor = selectedPricelistIds.competitor ? getPricelistVersionById('competitor', selectedPricelistIds.competitor) || `ID ${selectedPricelistIds.competitor}` : 'авто';
    const refreshState = disablePriceRefresh ? ' | Обновление цен: выкл' : '';
    const stockFilterState = onlyInStock ? ' | Только наличие: вкл' : '';
    summary.textContent = `Estimate: ${estimate}, Склад: ${warehouse}, Конкуренты: ${competitor}${refreshState}${stockFilterState}`;
}

function updateRefreshPricesButtonState() {
    const refreshBtn = document.getElementById('refresh-prices-btn');
    if (!refreshBtn) return;
    if (disablePriceRefresh) {
        refreshBtn.disabled = true;
        refreshBtn.className = 'px-4 py-2 bg-gray-300 text-gray-500 rounded cursor-not-allowed';
        refreshBtn.title = 'Обновление цен отключено в настройках';
    } else {
        refreshBtn.disabled = false;
        refreshBtn.className = 'px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700';
        refreshBtn.title = '';
    }
}

function getPriceSettingsStorageKey() {
    return `qf_price_settings_${configUUID || 'default'}`;
}

function persistLocalPriceSettings() {
    try {
        localStorage.setItem(getPriceSettingsStorageKey(), JSON.stringify({
            pricelist_ids: selectedPricelistIds,
            disable_price_refresh: disablePriceRefresh
        }));
    } catch (e) {
        // ignore localStorage failures
    }
}

function restoreLocalPriceSettings() {
    try {
        const raw = localStorage.getItem(getPriceSettingsStorageKey());
        if (!raw) return;
        const parsed = JSON.parse(raw);
        if (parsed && parsed.pricelist_ids) {
            ['estimate', 'warehouse', 'competitor'].forEach(source => {
                const next = parseInt(parsed.pricelist_ids[source]);
                if (Number.isFinite(next) && next > 0) {
                    selectedPricelistIds[source] = next;
                }
            });
        }
        disablePriceRefresh = Boolean(parsed?.disable_price_refresh);
    } catch (e) {
        // ignore invalid localStorage payload
    }
}

function openPriceSettingsModal() {
    syncPriceSettingsControls();
    renderPricelistSettingsSummary();
    document.getElementById('price-settings-modal')?.classList.remove('hidden');
    loadActivePricelists().then(() => {
        syncPriceSettingsControls();
        renderPricelistSettingsSummary();
    });
}

function closePriceSettingsModal() {
    document.getElementById('price-settings-modal')?.classList.add('hidden');
}

function applyPriceSettings() {
    const estimateVal = parseInt(document.getElementById('settings-pricelist-estimate')?.value || '');
    const warehouseVal = parseInt(document.getElementById('settings-pricelist-warehouse')?.value || '');
    const competitorVal = parseInt(document.getElementById('settings-pricelist-competitor')?.value || '');
    const disableVal = Boolean(document.getElementById('settings-disable-price-refresh')?.checked);
    const inStockVal = Boolean(document.getElementById('settings-only-in-stock')?.checked);

    const prevWarehouseID = currentWarehousePricelistID();
    selectedPricelistIds.estimate = Number.isFinite(estimateVal) && estimateVal > 0 ? estimateVal : null;
    selectedPricelistIds.warehouse = Number.isFinite(warehouseVal) && warehouseVal > 0 ? warehouseVal : null;
    selectedPricelistIds.competitor = Number.isFinite(competitorVal) && competitorVal > 0 ? competitorVal : null;
    if (selectedPricelistIds.estimate) {
        resolvedAutoPricelistIds.estimate = null;
    }
    if (selectedPricelistIds.warehouse) {
        resolvedAutoPricelistIds.warehouse = null;
    }
    if (selectedPricelistIds.competitor) {
        resolvedAutoPricelistIds.competitor = null;
    }
    disablePriceRefresh = disableVal;
    onlyInStock = inStockVal;

    const nextWarehouseID = currentWarehousePricelistID();
    if (Number.isFinite(prevWarehouseID) && prevWarehouseID > 0 && prevWarehouseID !== nextWarehouseID) {
        warehouseStockLotsByPricelist.delete(prevWarehouseID);
    }
    if (onlyInStock) {
        ensureWarehouseStockFilterLoaded().then(() => {
            renderTab();
            updateCartUI();
        });
    }

    updateRefreshPricesButtonState();
    renderPricelistSettingsSummary();
    persistLocalPriceSettings();
    closePriceSettingsModal();
    schedulePriceLevelsRefresh({ delay: 0, rerender: true, autosave: true });
}

function getCategoryFromLotName(lotName) {
    const parts = lotName.split('_');
    return parts[0] || '';
}

function getComponentCategory(comp) {
    return (comp.category || getCategoryFromLotName(comp.lot_name)).toUpperCase();
}

function getTabForCategory(category) {
    const cat = category.toUpperCase();
    for (const [tabKey, tabConfig] of Object.entries(TAB_CONFIG)) {
        if (tabConfig.categories.map(c => c.toUpperCase()).includes(cat)) {
            return tabKey;
        }
    }
    return 'other';
}

function switchTab(tab) {
    currentTab = tab;
    document.querySelectorAll('.tab-btn').forEach(btn => {
        if (btn.dataset.tab === tab) {
            btn.classList.remove('border-transparent', 'text-gray-500');
            btn.classList.add('border-blue-600', 'text-blue-600');
        } else {
            btn.classList.add('border-transparent', 'text-gray-500');
            btn.classList.remove('border-blue-600', 'text-blue-600');
        }
    });
    hideAutocomplete();
    renderTab();
}

function getComponentsForTab(tab) {
    const config = TAB_CONFIG[tab];
    return allComponents.filter(comp => {
        const category = getComponentCategory(comp);
        if (tab === 'other') {
            return !ASSIGNED_CATEGORIES.includes(category);
        }
        return config.categories.map(c => c.toUpperCase()).includes(category);
    });
}

function getComponentsForCategory(category) {
    return allComponents.filter(comp => {
        return getComponentCategory(comp) === category.toUpperCase();
    });
}

function renderTab() {
    const config = TAB_CONFIG[currentTab];
    const components = getComponentsForTab(currentTab);

    if (config.singleSelect) {
        renderSingleSelectTab(config.categories);
    } else if (config.sections) {
        renderMultiSelectTabWithSections(config.sections);
    } else {
        renderMultiSelectTab(components);
    }
}

function renderSingleSelectTab(categories) {
    let html = '';
    if (currentTab === 'base') {
        html += `
        <div class="mb-1 grid grid-cols-1 md:grid-cols-[1fr,16rem] gap-3 items-start">
            <label for="server-model-input" class="block text-sm font-medium text-gray-700">Модель сервера для КП:</label>
            <label for="support-code-select" class="block text-sm font-medium text-gray-700">Уровень техподдержки:</label>
        </div>
        <div class="mb-3 grid grid-cols-1 md:grid-cols-[1fr,16rem] gap-3 items-start">
            <input type="text"
                   id="server-model-input"
                   value="${escapeHtml(serverModelForQuote)}"
                   class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"
                   oninput="updateServerModelForQuote(this.value)">
            <select id="support-code-select"
                    class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500"
                    onchange="updateSupportCode(this.value)">
                <option value="">—</option>
                <option value="1yW" ${supportCode === '1yW' ? 'selected' : ''}>1yW</option>
                <option value="1yB" ${supportCode === '1yB' ? 'selected' : ''}>1yB</option>
                <option value="1yS" ${supportCode === '1yS' ? 'selected' : ''}>1yS</option>
                <option value="1yP" ${supportCode === '1yP' ? 'selected' : ''}>1yP</option>
            </select>
        </div>
        `;
    }
    html += `
        <table class="w-full">
            <thead class="bg-gray-50">
                <tr>
                    <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase w-24">Тип</th>
                    <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">LOT</th>
                    <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">Описание</th>
                    <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase w-24">Estimate</th>
                    <th class="px-3 py-2 text-center text-xs font-medium text-gray-500 uppercase w-20">Кол-во</th>
                    <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase w-28">Стоимость</th>
                    <th class="px-3 py-2 w-10"></th>
                </tr>
            </thead>
            <tbody class="divide-y">
    `;

    categories.forEach(cat => {
        const catLabel = cat === 'MB' ? 'MB' : cat === 'CPU' ? 'CPU' : cat === 'MEM' ? 'MEM' : cat;
        const selectedItem = cart.find(item =>
            (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase() === cat.toUpperCase()
        );

        const comp = selectedItem ? allComponents.find(c => c.lot_name === selectedItem.lot_name) : null;
        const price = comp?.current_price || 0;
        const estimate = selectedItem?.estimate_price ?? price;
        const qty = selectedItem?.quantity || 1;
        const total = (selectedItem ? getDisplayPrice(selectedItem) : price) * qty;

        html += `
            <tr class="hover:bg-gray-50">
                <td class="px-3 py-2 text-sm font-medium text-gray-700">${catLabel}</td>
                <td class="px-3 py-2">
                    <div class="autocomplete-wrapper relative">
                        <input type="text"
                               id="input-${cat}"
                               value="${selectedItem?.lot_name || ''}"
                               placeholder="Введите артикул..."
                               class="w-full px-2 py-1 border rounded text-sm font-mono"
                               onfocus="showAutocomplete('${cat}', this)"
                               oninput="filterAutocomplete('${cat}', this.value)"
                               onkeydown="handleAutocompleteKey(event, '${cat}')">
                    </div>
                </td>
                <td class="px-3 py-2 text-sm text-gray-500 truncate max-w-xs" id="desc-${cat}">${escapeHtml(comp?.description || '')}</td>
                <td class="px-3 py-2 text-sm text-right" id="price-${cat}">${formatPriceOrNA(estimate)}</td>
                <td class="px-3 py-2 text-center">
                    <input type="number" min="1" value="${qty}"
                           id="qty-${cat}"
                           onchange="updateSingleQuantity('${cat}', this.value)"
                           class="w-16 px-2 py-1 border rounded text-center text-sm">
                </td>
                <td class="px-3 py-2 text-sm text-right font-medium" id="total-${cat}">${total ? formatMoney(total) : '—'}</td>
                <td class="px-3 py-2 text-center">
                    ${selectedItem ? `
                        <button onclick="clearSingleSelect('${cat}')" class="text-red-500 hover:text-red-700">
                            <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
                            </svg>
                        </button>
                    ` : ''}
                </td>
            </tr>
        `;
    });

    html += '</tbody></table>';
    document.getElementById('tab-content').innerHTML = html;
}

function renderMultiSelectTab(components) {
    // Get cart items for this tab
    const tabItems = cart.filter(item => {
        const cat = (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase();
        const tab = getTabForCategory(cat);
        return tab === currentTab;
    });

    let html = `
        <table class="w-full">
            <thead class="bg-gray-50">
                <tr>
                    <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">LOT</th>
                    <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">Описание</th>
                    <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase w-24">Estimate</th>
                    <th class="px-3 py-2 text-center text-xs font-medium text-gray-500 uppercase w-20">Кол-во</th>
                    <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase w-28">Стоимость</th>
                    <th class="px-3 py-2 w-10"></th>
                </tr>
            </thead>
            <tbody class="divide-y">
    `;

    // Render existing cart items for this tab
    tabItems.forEach((item, idx) => {
        const comp = allComponents.find(c => c.lot_name === item.lot_name);
        const total = getDisplayPrice(item) * item.quantity;

        html += `
            <tr class="hover:bg-gray-50">
                <td class="px-3 py-2 text-sm font-mono">${escapeHtml(item.lot_name)}</td>
                <td class="px-3 py-2 text-sm text-gray-500 truncate max-w-xs">${escapeHtml(item.description || comp?.description || '')}</td>
                <td class="px-3 py-2 text-sm text-right">${formatPriceOrNA(item.estimate_price ?? item.unit_price)}</td>
                <td class="px-3 py-2 text-center">
                    <input type="number" min="1" value="${item.quantity}"
                           onchange="updateMultiQuantity('${item.lot_name}', this.value)"
                           class="w-16 px-2 py-1 border rounded text-center text-sm">
                </td>
                <td class="px-3 py-2 text-sm text-right font-medium">${formatMoney(total)}</td>
                <td class="px-3 py-2 text-center">
                    <button onclick="removeFromCart('${item.lot_name}')" class="text-red-500 hover:text-red-700">
                        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
                        </svg>
                    </button>
                </td>
            </tr>
        `;
    });

    // Add empty row for new item
    html += `
        <tr class="hover:bg-gray-50 bg-gray-50">
            <td class="px-3 py-2" colspan="2">
                <div class="autocomplete-wrapper relative">
                    <input type="text"
                           id="input-new-item"
                           placeholder="Добавить компонент..."
                           class="w-full px-2 py-1 border rounded text-sm"
                           onfocus="showAutocompleteMulti(this)"
                           oninput="filterAutocompleteMulti(this.value)"
                           onkeydown="handleAutocompleteKeyMulti(event)">
                </div>
            </td>
            <td class="px-3 py-2 text-sm text-right text-gray-400" id="new-price">N/A</td>
            <td class="px-3 py-2 text-center">
                <input type="number" min="1" value="1" id="new-qty"
                       class="w-16 px-2 py-1 border rounded text-center text-sm">
            </td>
            <td class="px-3 py-2 text-sm text-right text-gray-400" id="new-total">—</td>
            <td class="px-3 py-2"></td>
        </tr>
    `;

    html += '</tbody></table>';
    html += `<p class="text-center text-sm text-gray-500 mt-4">Доступно компонентов: ${components.length}</p>`;

    document.getElementById('tab-content').innerHTML = html;
}

function renderMultiSelectTabWithSections(sections) {
    // Get cart items for this tab
    const tabItems = cart.filter(item => {
        const cat = (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase();
        const tab = getTabForCategory(cat);
        return tab === currentTab;
    });

    let html = '';
    let totalComponents = 0;

    sections.forEach((section, sectionIdx) => {
        // Get components for this section's categories
        const sectionCategories = section.categories.map(c => c.toUpperCase());
        const sectionComponents = allComponents.filter(comp => {
            const category = getComponentCategory(comp);
            return sectionCategories.includes(category);
        });
        totalComponents += sectionComponents.length;

        // Get cart items for this section
        const sectionItems = tabItems.filter(item => {
            const cat = (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase();
            return sectionCategories.includes(cat);
        });

        // Section header
        html += `
            <div class="mb-6 ${sectionIdx > 0 ? 'mt-6' : ''}">
                <h3 class="text-sm font-semibold text-gray-700 mb-3 px-3">${section.title}</h3>
                <table class="w-full">
                    <thead class="bg-gray-50">
                        <tr>
                            <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">LOT</th>
                            <th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">Описание</th>
                            <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase w-24">Estimate</th>
                            <th class="px-3 py-2 text-center text-xs font-medium text-gray-500 uppercase w-20">Кол-во</th>
                            <th class="px-3 py-2 text-right text-xs font-medium text-gray-500 uppercase w-28">Стоимость</th>
                            <th class="px-3 py-2 w-10"></th>
                        </tr>
                    </thead>
                    <tbody class="divide-y">
        `;

        // Render existing cart items for this section
        sectionItems.forEach((item) => {
            const comp = allComponents.find(c => c.lot_name === item.lot_name);
            const total = getDisplayPrice(item) * item.quantity;

            html += `
                <tr class="hover:bg-gray-50">
                    <td class="px-3 py-2 text-sm font-mono">${escapeHtml(item.lot_name)}</td>
                    <td class="px-3 py-2 text-sm text-gray-500 truncate max-w-xs">${escapeHtml(item.description || comp?.description || '')}</td>
                    <td class="px-3 py-2 text-sm text-right">${formatPriceOrNA(item.estimate_price ?? item.unit_price)}</td>
                    <td class="px-3 py-2 text-center">
                        <input type="number" min="1" value="${item.quantity}"
                               onchange="updateMultiQuantity('${item.lot_name}', this.value)"
                               class="w-16 px-2 py-1 border rounded text-center text-sm">
                    </td>
                    <td class="px-3 py-2 text-sm text-right font-medium">${formatMoney(total)}</td>
                    <td class="px-3 py-2 text-center">
                        <button onclick="removeFromCart('${item.lot_name}')" class="text-red-500 hover:text-red-700">
                            <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
                            </svg>
                        </button>
                    </td>
                </tr>
            `;
        });

        // Add empty row for new item in this section
        const sectionId = section.categories.join('-');
        const categoriesStr = section.categories.join(',');
        html += `
            <tr class="hover:bg-gray-50 bg-gray-50">
                <td class="px-3 py-2" colspan="2">
                    <div class="autocomplete-wrapper relative">
                        <input type="text"
                               id="input-section-${sectionId}"
                               data-categories="${categoriesStr}"
                               placeholder="Добавить ${section.title.toLowerCase()}..."
                               class="w-full px-2 py-1 border rounded text-sm"
                               onfocus="showAutocompleteSection('${sectionId}', this)"
                               oninput="filterAutocompleteSection('${sectionId}', this.value, this)"
                               onkeydown="handleAutocompleteKeySection(event, '${sectionId}')">
                    </div>
                </td>
                <td class="px-3 py-2 text-sm text-right text-gray-400" id="new-price-${sectionId}">N/A</td>
                <td class="px-3 py-2 text-center">
                    <input type="number" min="1" value="1" id="new-qty-${sectionId}"
                           class="w-16 px-2 py-1 border rounded text-center text-sm">
                </td>
                <td class="px-3 py-2 text-sm text-right text-gray-400" id="new-total-${sectionId}">—</td>
                <td class="px-3 py-2"></td>
            </tr>
        `;

        html += `
                    </tbody>
                </table>
                <p class="text-center text-sm text-gray-500 mt-2">Доступно: ${sectionComponents.length}</p>
            </div>
        `;
    });

    document.getElementById('tab-content').innerHTML = html;
}

// Load prices for components in a category/tab via API
async function ensurePricesLoaded(components) {
    if (!components || components.length === 0) return;

    // Filter out components that already have prices cached
    const toLoad = components.filter(c => !(c.lot_name in componentPricesCache));
    if (toLoad.length === 0) return;

    try {
        // Use quote/price-levels API to get prices for these components
        const resp = await fetch('/api/quote/price-levels', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                items: toLoad.map(c => ({ lot_name: c.lot_name, quantity: 1 })),
                pricelist_ids: Object.fromEntries(
                    Object.entries(selectedPricelistIds)
                        .filter(([, id]) => typeof id === 'number' && id > 0)
                )
            })
        });

        if (resp.ok) {
            const data = await resp.json();
            if (data.items) {
                data.items.forEach(item => {
                    // Cache the estimate price (or 0 if not found)
                    componentPricesCache[item.lot_name] = item.estimate_price || 0;
                });
            }
        }
    } catch (e) {
        console.error('Failed to load component prices', e);
    }
}

function hasComponentPrice(lotName) {
    return lotName in componentPricesCache && componentPricesCache[lotName] > 0;
}

// Autocomplete for single select (Base tab)
async function showAutocomplete(category, input) {
    autocompleteInput = input;
    autocompleteCategory = category;
    autocompleteMode = 'single';
    autocompleteIndex = -1;
    const components = getComponentsForCategory(category);
    await ensurePricesLoaded(components);
    filterAutocomplete(category, input.value);
}

function filterAutocomplete(category, search) {
    const components = getComponentsForCategory(category);
    const searchLower = search.toLowerCase();

    autocompleteFiltered = components.filter(c => {
        if (!hasComponentPrice(c.lot_name)) return false;
        if (!isComponentAllowedByStockFilter(c)) return false;
        const text = (c.lot_name + ' ' + (c.description || '')).toLowerCase();
        return text.includes(searchLower);
    })
    .sort((a, b) => {
        // Sort by popularity_score desc, then by lot_name
        const popDiff = (b.popularity_score || 0) - (a.popularity_score || 0);
        if (popDiff !== 0) return popDiff;
        return a.lot_name.localeCompare(b.lot_name);
    });

    renderAutocomplete();
}

function renderAutocomplete() {
    const dropdown = document.getElementById('autocomplete-dropdown');

    if (autocompleteFiltered.length === 0 || !autocompleteInput) {
        dropdown.classList.add('hidden');
        return;
    }

    const rect = autocompleteInput.getBoundingClientRect();
    dropdown.style.top = (rect.bottom + window.scrollY) + 'px';
    dropdown.style.left = rect.left + 'px';
    dropdown.style.width = Math.max(rect.width, 400) + 'px';

    // Build autocomplete items based on mode
    dropdown.innerHTML = autocompleteFiltered.map((comp, idx) => {
        let onmousedown;

        if (autocompleteMode === 'section') {
            onmousedown = `selectAutocompleteItemSection(${idx}, '${autocompleteCategory}')`;
        } else if (autocompleteMode === 'multi') {
            onmousedown = `selectAutocompleteItemMulti(${idx})`;
        } else {
            // single mode
            onmousedown = `selectAutocompleteItem(${idx})`;
        }

        return `
            <div class="autocomplete-item ${idx === autocompleteIndex ? 'selected' : ''}"
                 onmousedown="${onmousedown}">
                <div class="font-mono text-sm">${escapeHtml(comp.lot_name)}</div>
                <div class="text-xs text-gray-500 truncate">${escapeHtml(comp.description || '')}</div>
            </div>
        `;
    }).join('');

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

function handleAutocompleteKey(event, category) {
    if (event.key === 'ArrowDown') {
        event.preventDefault();
        autocompleteIndex = Math.min(autocompleteIndex + 1, autocompleteFiltered.length - 1);
        renderAutocomplete();
    } else if (event.key === 'ArrowUp') {
        event.preventDefault();
        autocompleteIndex = Math.max(autocompleteIndex - 1, -1);
        renderAutocomplete();
    } else if (event.key === 'Enter') {
        event.preventDefault();
        if (autocompleteIndex >= 0 && autocompleteIndex < autocompleteFiltered.length) {
            selectAutocompleteItem(autocompleteIndex);
        }
    } else if (event.key === 'Escape') {
        hideAutocomplete();
    }
}

function selectAutocompleteItem(index) {
    const comp = autocompleteFiltered[index];
    if (!comp || !autocompleteCategory) return;

    // Remove existing item of this category
    cart = cart.filter(item =>
        (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase() !== autocompleteCategory.toUpperCase()
    );

    const qtyInput = document.getElementById('qty-' + autocompleteCategory);
    const qty = parseInt(qtyInput?.value) || 1;
    const price = componentPricesCache[comp.lot_name] || 0;

    cart.push({
        lot_name: comp.lot_name,
        quantity: qty,
        unit_price: price,
        estimate_price: price,
        warehouse_price: null,
        competitor_price: null,
        delta_wh_estimate_abs: null,
        delta_wh_estimate_pct: null,
        delta_comp_estimate_abs: null,
        delta_comp_estimate_pct: null,
        delta_comp_wh_abs: null,
        delta_comp_wh_pct: null,
        price_missing: ['warehouse', 'competitor'],
        description: comp.description || '',
        category: getComponentCategory(comp)
    });

    hideAutocomplete();
    renderTab();
    updateCartUI();
    triggerAutoSave();
    schedulePriceLevelsRefresh({ delay: 80, rerender: true, autosave: false });
}

function hideAutocomplete() {
    document.getElementById('autocomplete-dropdown').classList.add('hidden');
    autocompleteInput = null;
    autocompleteCategory = null;
    autocompleteMode = null;
    autocompleteIndex = -1;
}

// Autocomplete for multi select tabs
async function showAutocompleteMulti(input) {
    autocompleteInput = input;
    autocompleteCategory = null;
    autocompleteMode = 'multi';
    autocompleteIndex = -1;
    const components = getComponentsForTab(currentTab);
    await ensurePricesLoaded(components);
    filterAutocompleteMulti(input.value);
}

function filterAutocompleteMulti(search) {
    const components = getComponentsForTab(currentTab);
    const searchLower = search.toLowerCase();

    // Filter out already added items
    const addedLots = new Set(cart.map(i => i.lot_name));

    autocompleteFiltered = components.filter(c => {
        if (!hasComponentPrice(c.lot_name)) return false;
        if (addedLots.has(c.lot_name)) return false;
        if (!isComponentAllowedByStockFilter(c)) return false;
        const text = (c.lot_name + ' ' + (c.description || '')).toLowerCase();
        return text.includes(searchLower);
    })
    .sort((a, b) => {
        // Sort by popularity_score desc, then by lot_name
        const popDiff = (b.popularity_score || 0) - (a.popularity_score || 0);
        if (popDiff !== 0) return popDiff;
        return a.lot_name.localeCompare(b.lot_name);
    });

    renderAutocomplete();
}

function handleAutocompleteKeyMulti(event) {
    if (event.key === 'ArrowDown') {
        event.preventDefault();
        autocompleteIndex = Math.min(autocompleteIndex + 1, autocompleteFiltered.length - 1);
        renderAutocomplete();
    } else if (event.key === 'ArrowUp') {
        event.preventDefault();
        autocompleteIndex = Math.max(autocompleteIndex - 1, -1);
        renderAutocomplete();
    } else if (event.key === 'Enter') {
        event.preventDefault();
        if (autocompleteIndex >= 0 && autocompleteIndex < autocompleteFiltered.length) {
            selectAutocompleteItemMulti(autocompleteIndex);
        }
    } else if (event.key === 'Escape') {
        hideAutocomplete();
    }
}

function selectAutocompleteItemMulti(index) {
    const comp = autocompleteFiltered[index];
    if (!comp) return;

    const qtyInput = document.getElementById('new-qty');
    const qty = parseInt(qtyInput?.value) || 1;
    const price = componentPricesCache[comp.lot_name] || 0;

    cart.push({
        lot_name: comp.lot_name,
        quantity: qty,
        unit_price: price,
        estimate_price: price,
        warehouse_price: null,
        competitor_price: null,
        delta_wh_estimate_abs: null,
        delta_wh_estimate_pct: null,
        delta_comp_estimate_abs: null,
        delta_comp_estimate_pct: null,
        delta_comp_wh_abs: null,
        delta_comp_wh_pct: null,
        price_missing: ['warehouse', 'competitor'],
        description: comp.description || '',
        category: getComponentCategory(comp)
    });

    hideAutocomplete();
    renderTab();
    updateCartUI();
    triggerAutoSave();
    schedulePriceLevelsRefresh({ delay: 80, rerender: true, autosave: false });
}

// Autocomplete for sectioned tabs (like storage with RAID and Disks sections)
async function showAutocompleteSection(sectionId, input) {
    autocompleteInput = input;
    autocompleteCategory = sectionId; // Store section ID
    autocompleteMode = 'section';
    autocompleteIndex = -1;

    // Load prices for tab components
    const components = getComponentsForTab(currentTab);
    await ensurePricesLoaded(components);

    filterAutocompleteSection(sectionId, input.value, input);
}

function filterAutocompleteSection(sectionId, search, inputElement) {
    const searchLower = search.toLowerCase();

    // Get categories from input element's data attribute
    const categoriesStr = inputElement && inputElement.dataset ? inputElement.dataset.categories : '';
    if (!categoriesStr) {
        autocompleteFiltered = [];
        renderAutocomplete();
        return;
    }

    const categoryList = categoriesStr.split(',').map(c => c.trim().toUpperCase());

    // Get components for this section's categories
    const sectionComponents = allComponents.filter(comp => {
        const category = getComponentCategory(comp);
        return categoryList.includes(category);
    });

    // Filter out already added items
    const addedLots = new Set(cart.map(i => i.lot_name));

    autocompleteFiltered = sectionComponents.filter(c => {
        if (!hasComponentPrice(c.lot_name)) return false;
        if (addedLots.has(c.lot_name)) return false;
        if (!isComponentAllowedByStockFilter(c)) return false;
        const text = (c.lot_name + ' ' + (c.description || '')).toLowerCase();
        return text.includes(searchLower);
    })
    .sort((a, b) => {
        // Sort by popularity_score desc, then by lot_name
        const popDiff = (b.popularity_score || 0) - (a.popularity_score || 0);
        if (popDiff !== 0) return popDiff;
        return a.lot_name.localeCompare(b.lot_name);
    });

    renderAutocomplete();
}

function handleAutocompleteKeySection(event, sectionId) {
    if (event.key === 'ArrowDown') {
        event.preventDefault();
        autocompleteIndex = Math.min(autocompleteIndex + 1, autocompleteFiltered.length - 1);
        renderAutocomplete();
    } else if (event.key === 'ArrowUp') {
        event.preventDefault();
        autocompleteIndex = Math.max(autocompleteIndex - 1, -1);
        renderAutocomplete();
    } else if (event.key === 'Enter') {
        event.preventDefault();
        if (autocompleteIndex >= 0 && autocompleteIndex < autocompleteFiltered.length) {
            selectAutocompleteItemSection(autocompleteIndex, sectionId);
        }
    } else if (event.key === 'Escape') {
        hideAutocomplete();
    }
}

function selectAutocompleteItemSection(index, sectionId) {
    const comp = autocompleteFiltered[index];
    if (!comp) return;

    const qtyInput = document.getElementById('new-qty-' + sectionId);
    const qty = parseInt(qtyInput?.value) || 1;
    const price = componentPricesCache[comp.lot_name] || 0;

    cart.push({
        lot_name: comp.lot_name,
        quantity: qty,
        unit_price: price,
        estimate_price: price,
        warehouse_price: null,
        competitor_price: null,
        delta_wh_estimate_abs: null,
        delta_wh_estimate_pct: null,
        delta_comp_estimate_abs: null,
        delta_comp_estimate_pct: null,
        delta_comp_wh_abs: null,
        delta_comp_wh_pct: null,
        price_missing: ['warehouse', 'competitor'],
        description: comp.description || '',
        category: getComponentCategory(comp)
    });

    hideAutocomplete();

    // Clear the input field
    const input = document.getElementById('input-section-' + sectionId);
    if (input) input.value = '';

    // Reset quantity to 1
    if (qtyInput) qtyInput.value = '1';
    renderTab();
    updateCartUI();
    triggerAutoSave();
    schedulePriceLevelsRefresh({ delay: 80, rerender: true, autosave: false });
}

function clearSingleSelect(category) {
    cart = cart.filter(item =>
        (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase() !== category.toUpperCase()
    );
    renderTab();
    updateCartUI();
    triggerAutoSave();
}

function updateSingleQuantity(category, value) {
    const qty = parseInt(value) || 1;
    const item = cart.find(i =>
        (i.category || getCategoryFromLotName(i.lot_name)).toUpperCase() === category.toUpperCase()
    );

    if (item) {
        item.quantity = Math.max(1, qty);
        renderTab();
        updateCartUI();
        triggerAutoSave();
    }
}

function updateMultiQuantity(lotName, value) {
    const qty = parseInt(value) || 1;
    const item = cart.find(i => i.lot_name === lotName);

    if (item) {
        item.quantity = Math.max(1, qty);
        updateCartUI();
        triggerAutoSave();
        // Update total in the row
        const row = document.querySelector(`input[onchange*="${lotName}"]`)?.closest('tr');
        if (row) {
            const totalCell = row.querySelector('td:nth-child(5)');
            if (totalCell) {
                totalCell.textContent = formatMoney(getDisplayPrice(item) * item.quantity);
            }
        }
    }
}

function removeFromCart(lotName) {
    cart = cart.filter(i => i.lot_name !== lotName);
    renderTab();
    updateCartUI();
    triggerAutoSave();
}

function updateCartUI() {
    const total = cart.reduce((sum, item) => sum + (getDisplayPrice(item) * item.quantity), 0);
    document.getElementById('cart-total').textContent = formatMoney(total);

    // Recalculate custom price section if active
    calculateCustomPrice();
    renderSalePriceTable();

    scheduleArticlePreview();

    if (cart.length === 0) {
        document.getElementById('cart-items').innerHTML =
            '<div class="text-gray-500 text-center py-2">Конфигурация пуста</div>';
        return;
    }

    // Sort cart items by category display order
    const sortedCart = [...cart].sort((a, b) => {
        const catA = (a.category || getCategoryFromLotName(a.lot_name)).toUpperCase();
        const catB = (b.category || getCategoryFromLotName(b.lot_name)).toUpperCase();
        const orderA = categoryOrderMap[catA] || 9999;
        const orderB = categoryOrderMap[catB] || 9999;
        return orderA - orderB;
    });

    const grouped = {};
    sortedCart.forEach(item => {
        const cat = item.category || getCategoryFromLotName(item.lot_name);
        const tab = getTabForCategory(cat);
        if (!grouped[tab]) grouped[tab] = [];
        grouped[tab].push(item);
    });

    // Sort tabs by minimum display order of their categories
    const sortedTabs = Object.entries(grouped).sort((a, b) => {
        const minOrderA = Math.min(...a[1].map(item => {
            const cat = (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase();
            return categoryOrderMap[cat] || 9999;
        }));
        const minOrderB = Math.min(...b[1].map(item => {
            const cat = (item.category || getCategoryFromLotName(item.lot_name)).toUpperCase();
            return categoryOrderMap[cat] || 9999;
        }));
        return minOrderA - minOrderB;
    });

    let html = '';
    for (const [tab, items] of sortedTabs) {
        const tabLabel = TAB_CONFIG[tab]?.label || tab;
        html += `<div class="mb-2"><div class="text-xs font-medium text-gray-400 uppercase mb-1">${tabLabel}</div>`;

        items.forEach(item => {
            const itemTotal = getDisplayPrice(item) * item.quantity;
            html += `
                <div class="flex justify-between items-center py-1 text-sm">
                    <div class="flex-1">
                        <span class="font-mono">${escapeHtml(item.lot_name)}</span>
                        <span class="text-gray-400 mx-1">×</span>
                        <span>${item.quantity}</span>
                    </div>
                    <div class="flex items-center space-x-2">
                        <span>${formatMoney(itemTotal)}</span>
                        <button onclick="removeFromCart('${item.lot_name}')" class="text-red-500 hover:text-red-700">
                            <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
                            </svg>
                        </button>
                    </div>
                </div>
            `;
        });

        html += '</div>';
    }

    document.getElementById('cart-items').innerHTML = html;
}

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

function updateServerModelForQuote(value) {
    serverModelForQuote = value || '';
    scheduleArticlePreview();
}

function updateSupportCode(value) {
    supportCode = value || '';
    scheduleArticlePreview();
}

function scheduleArticlePreview() {
    if (articlePreviewTimeout) {
        clearTimeout(articlePreviewTimeout);
    }
    articlePreviewTimeout = setTimeout(() => {
        previewArticle();
    }, 250);
}

async function previewArticle() {
    const el = document.getElementById('article-display');
    if (!el) return;

    const model = serverModelForQuote.trim();
    const estimatePricelistID = getEffectivePricelistID('estimate');
    if (!model || !estimatePricelistID || cart.length === 0) {
        currentArticle = '';
        el.textContent = 'Артикул: —';
        return;
    }

    try {
        const resp = await fetch('/api/configs/preview-article', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({
                server_model: serverModelForQuote,
                support_code: supportCode,
                pricelist_id: estimatePricelistID,
                items: cart.map(item => ({
                    lot_name: item.lot_name,
                    quantity: item.quantity,
                    unit_price: item.unit_price || 0
                }))
            })
        });
        if (!resp.ok) {
            currentArticle = '';
            el.textContent = 'Артикул: —';
            return;
        }
        const data = await resp.json();
        currentArticle = data.article || '';
        el.textContent = currentArticle ? ('Артикул: ' + currentArticle) : 'Артикул: —';
    } catch(e) {
        currentArticle = '';
        el.textContent = 'Артикул: —';
    }
}

function getCurrentArticle() {
    return currentArticle || '';
}

function getAutosaveStorageKey() {
    return `qf_config_autosave_${configUUID || 'default'}`;
}

function buildSavePayload() {
    const customPriceInput = document.getElementById('custom-price-input');
    const customPriceValue = parseFloat(customPriceInput.value);
    const customPrice = customPriceValue > 0 ? customPriceValue : null;

    return {
        name: configName,
        items: cart,
        custom_price: customPrice,
        notes: '',
        server_count: serverCount,
        server_model: serverModelForQuote,
        support_code: supportCode,
        article: getCurrentArticle(),
        pricelist_id: selectedPricelistIds.estimate,
        only_in_stock: onlyInStock
    };
}

function persistAutosaveDraft() {
    if (!configUUID) return;
    try {
        sessionStorage.setItem(getAutosaveStorageKey(), JSON.stringify({
            payload: buildSavePayload(),
            saved_at: Date.now()
        }));
    } catch (_) {
        // ignore storage failures
    }
}

function clearAutosaveDraft() {
    try {
        sessionStorage.removeItem(getAutosaveStorageKey());
    } catch (_) {
        // ignore storage failures
    }
}

function restoreAutosaveDraftIfAny() {
    if (!configUUID) return;
    let raw = null;
    try {
        raw = sessionStorage.getItem(getAutosaveStorageKey());
    } catch (_) {
        raw = null;
    }
    if (!raw) return;

    try {
        const parsed = JSON.parse(raw);
        const payload = parsed && parsed.payload ? parsed.payload : null;
        if (!payload) return;

        if (Array.isArray(payload.items)) {
            cart = payload.items.map(item => ({
                lot_name: item.lot_name,
                quantity: item.quantity,
                unit_price: item.unit_price,
                estimate_price: item.unit_price,
                warehouse_price: null,
                competitor_price: null,
                description: item.description || '',
                category: item.category || getCategoryFromLotName(item.lot_name)
            }));
        }
        if (typeof payload.server_count === 'number' && payload.server_count > 0) {
            serverCount = payload.server_count;
            const serverCountInput = document.getElementById('server-count');
            if (serverCountInput) serverCountInput.value = serverCount;
            const totalServerCount = document.getElementById('total-server-count');
            if (totalServerCount) totalServerCount.textContent = serverCount;
        }
        serverModelForQuote = payload.server_model || serverModelForQuote;
        supportCode = payload.support_code || supportCode;
        currentArticle = payload.article || currentArticle;
        selectedPricelistIds.estimate = payload.pricelist_id || selectedPricelistIds.estimate;
        onlyInStock = Boolean(payload.only_in_stock);

        const customPriceInput = document.getElementById('custom-price-input');
        if (customPriceInput) {
            if (typeof payload.custom_price === 'number' && payload.custom_price > 0) {
                customPriceInput.value = payload.custom_price.toFixed(2);
            } else {
                customPriceInput.value = '';
            }
        }
        hasUnsavedChanges = true;
    } catch (_) {
        // ignore invalid draft
    }
}

function saveConfigOnExit() {
    if (!configUUID || !hasUnsavedChanges || exitSaveStarted) return;
    exitSaveStarted = true;
    try {
        fetch('/api/configs/' + configUUID, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(buildSavePayload()),
            keepalive: true
        });
    } catch (_) {
        // best effort save on page exit
    }
}

function triggerAutoSave() {
    // Autosave keeps local draft only; server revision is created on Save/Exit.
    hasUnsavedChanges = true;
    if (autoSaveTimeout) {
        clearTimeout(autoSaveTimeout);
    }
    autoSaveTimeout = setTimeout(() => {
        persistAutosaveDraft();
    }, 1000);
}

async function saveConfig(showNotification = true) {
    // RBAC disabled - no token check required
    if (!configUUID) return;
    if (priceLevelsRefreshTimer) {
        clearTimeout(priceLevelsRefreshTimer);
        priceLevelsRefreshTimer = null;
    }

    await refreshPriceLevels({ force: true, noCache: true });

    try {
        const resp = await fetch('/api/configs/' + configUUID, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(buildSavePayload())
        });

        if (!resp.ok) {
            if (showNotification) {
                showToast('Ошибка сохранения', 'error');
            }
            return;
        }

        const saved = await resp.json();
        if (saved && saved.current_version_no) {
            currentVersionNo = saved.current_version_no;
            const versionEl = document.getElementById('breadcrumb-config-version');
            if (versionEl) versionEl.textContent = 'v' + currentVersionNo;
        }
        hasUnsavedChanges = false;
        clearAutosaveDraft();
        exitSaveStarted = false;

        if (showNotification) {
            showToast('Сохранено', 'success');
        }
    } catch(e) {
        if (showNotification) {
            showToast('Ошибка сохранения', 'error');
        }
    }
}

// Helper function to extract filename from Content-Disposition header
function getFilenameFromResponse(resp) {
    const contentDisposition = resp.headers.get('content-disposition');
    if (!contentDisposition) return null;
    const matches = contentDisposition.match(/filename="?([^"]+)"?/);
    return matches && matches[1] ? matches[1] : null;
}

async function exportCSV() {
    if (cart.length === 0) return;

    try {
        if (priceLevelsRefreshTimer) {
            clearTimeout(priceLevelsRefreshTimer);
            priceLevelsRefreshTimer = null;
        }
        await refreshPriceLevels({ force: true, noCache: true });

        const exportItems = cart.map(item => ({
            ...item,
            unit_price: getDisplayPrice(item),
        }));
        const article = getCurrentArticle();
        const resp = await fetch('/api/export/csv', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({items: exportItems, name: configName, project_uuid: projectUUID, article: article, server_count: serverCount, pricelist_id: selectedPricelistIds.estimate || null})
        });

        const blob = await resp.blob();
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        const articleForName = article || 'BOM';
        a.download = getFilenameFromResponse(resp) || ((configName || 'config') + ' ' + articleForName + '.csv');
        a.click();
        window.URL.revokeObjectURL(url);
    } catch(e) {
        showToast('Ошибка экспорта', 'error');
    }
}

function formatLineTotalTooltip(qty, unitPrice) {
    if (typeof unitPrice !== 'number' || unitPrice <= 0) return '';
    const lineTotal = qty * unitPrice;
    return `${formatNumberRu(qty)} * ${formatMoney(unitPrice)} = ${formatMoney(lineTotal)}`;
}

function toggleSection(contentId, iconId) {
    const content = document.getElementById(contentId);
    const icon = document.getElementById(iconId);
    if (!content || !icon) return;

    const isHidden = content.classList.toggle('hidden');
    if (isHidden) {
        icon.classList.add('-rotate-90');
    } else {
        icon.classList.remove('-rotate-90');
    }
}

function toggleCartSummarySection() {
    toggleSection('cart-summary-content', 'cart-summary-toggle-icon');
}

function toggleCustomPriceSection() {
    toggleSection('custom-price-content', 'custom-price-toggle-icon');
}

function toggleSalePriceSection() {
    toggleSection('sale-price-content', 'sale-price-toggle-icon');
}

function formatDiffPercent(baseTotal, compareTotal, compareLabel) {
    if (typeof baseTotal !== 'number' || typeof compareTotal !== 'number' || compareTotal <= 0) {
        return `N/A от ${compareLabel}`;
    }
    const pct = ((baseTotal - compareTotal) / compareTotal) * 100;
    const sign = pct > 0 ? '+' : '';
    return `${sign}${pct.toFixed(1)}% от ${compareLabel}`;
}

function getTotalClass(current, references) {
    const validRefs = references.filter(v => typeof v === 'number' && v > 0);
    if (typeof current !== 'number' || current <= 0 || validRefs.length === 0) {
        return 'text-gray-900';
    }
    const avg = validRefs.reduce((sum, v) => sum + v, 0) / validRefs.length;
    if (current < avg) return 'text-green-600';
    if (current > avg) return 'text-red-600';
    return 'text-gray-900';
}

function renderSalePriceTable() {
    const body = document.getElementById('sale-prices-body');
    const totalEstEl = document.getElementById('sale-total-est');
    const totalWarehouseEl = document.getElementById('sale-total-warehouse');
    const totalCompetitorEl = document.getElementById('sale-total-competitor');
    if (!body || !totalEstEl || !totalWarehouseEl || !totalCompetitorEl) return;

    if (cart.length === 0) {
        body.innerHTML = '<tr><td colspan="5" class="px-3 py-3 text-center text-gray-500">Конфигурация пуста</td></tr>';
        totalEstEl.textContent = '$0.00';
        totalWarehouseEl.textContent = '$0.00';
        totalCompetitorEl.textContent = '$0.00';
        totalEstEl.title = '';
        totalWarehouseEl.title = '';
        totalCompetitorEl.title = '';
        totalEstEl.className = 'px-3 py-2 text-right';
        totalWarehouseEl.className = 'px-3 py-2 text-right';
        totalCompetitorEl.className = 'px-3 py-2 text-right';
        return;
    }

    const sortedCart = [...cart].sort((a, b) => {
        const catA = (a.category || getCategoryFromLotName(a.lot_name)).toUpperCase();
        const catB = (b.category || getCategoryFromLotName(b.lot_name)).toUpperCase();
        const orderA = categoryOrderMap[catA] || 9999;
        const orderB = categoryOrderMap[catB] || 9999;
        return orderA - orderB;
    });

    let html = '';
    let totalEstimate = 0;
    let totalWarehouse = 0;
    let totalCompetitor = 0;

    sortedCart.forEach(item => {
        const qty = item.quantity || 1;
        const estPrice = item.estimate_price;
        const warehousePrice = item.warehouse_price;
        const competitorPrice = item.competitor_price;

        if (typeof estPrice === 'number' && estPrice > 0) totalEstimate += estPrice * qty;
        if (typeof warehousePrice === 'number' && warehousePrice > 0) totalWarehouse += warehousePrice * qty;
        if (typeof competitorPrice === 'number' && competitorPrice > 0) totalCompetitor += competitorPrice * qty;

        const estTooltip = formatLineTotalTooltip(qty, estPrice);
        const warehouseTooltip = formatLineTotalTooltip(qty, warehousePrice);
        const competitorTooltip = formatLineTotalTooltip(qty, competitorPrice);

        html += `
            <tr>
                <td class="px-3 py-2 font-mono">${escapeHtml(item.lot_name)}</td>
                <td class="px-3 py-2 text-right">${qty}</td>
                <td class="px-3 py-2 text-right" title="${escapeHtml(estTooltip)}">${formatPriceOrNA(estPrice)}</td>
                <td class="px-3 py-2 text-right" title="${escapeHtml(warehouseTooltip)}">${formatPriceOrNA(warehousePrice)}</td>
                <td class="px-3 py-2 text-right" title="${escapeHtml(competitorTooltip)}">${formatPriceOrNA(competitorPrice)}</td>
            </tr>
        `;
    });

    body.innerHTML = html;

    totalEstEl.textContent = formatMoney(totalEstimate);
    totalWarehouseEl.textContent = formatMoney(totalWarehouse);
    totalCompetitorEl.textContent = formatMoney(totalCompetitor);

    totalEstEl.title =
        `${formatDiffPercent(totalEstimate, totalWarehouse, 'склад')}\n` +
        `${formatDiffPercent(totalEstimate, totalCompetitor, 'конкуренты')}`;
    totalWarehouseEl.title =
        `${formatDiffPercent(totalWarehouse, totalEstimate, 'est.price')}\n` +
        `${formatDiffPercent(totalWarehouse, totalCompetitor, 'конкуренты')}`;
    totalCompetitorEl.title =
        `${formatDiffPercent(totalCompetitor, totalEstimate, 'est.price')}\n` +
        `${formatDiffPercent(totalCompetitor, totalWarehouse, 'склад')}`;

    totalEstEl.className = `px-3 py-2 text-right ${getTotalClass(totalEstimate, [totalWarehouse, totalCompetitor])}`;
    totalWarehouseEl.className = `px-3 py-2 text-right ${getTotalClass(totalWarehouse, [totalEstimate, totalCompetitor])}`;
    totalCompetitorEl.className = `px-3 py-2 text-right ${getTotalClass(totalCompetitor, [totalEstimate, totalWarehouse])}`;
}

// Custom price functionality
function calculateCustomPrice() {
    const customPriceInput = document.getElementById('custom-price-input');
    const customPrice = parseFloat(customPriceInput.value) || 0;

    const originalTotal = cart.reduce((sum, item) => sum + (getDisplayPrice(item) * item.quantity), 0);

    if (customPrice <= 0 || cart.length === 0 || originalTotal <= 0) {
        document.getElementById('adjusted-prices').classList.add('hidden');
        document.getElementById('discount-info').classList.add('hidden');
        return;
    }

    // Calculate discount percentage
    const discountPercent = ((originalTotal - customPrice) / originalTotal) * 100;
    const coefficient = customPrice / originalTotal;

    // Show discount info
    document.getElementById('discount-info').classList.remove('hidden');
    document.getElementById('discount-percent').textContent = discountPercent.toFixed(1) + '%';

    // Update discount color based on value
    const discountEl = document.getElementById('discount-percent');
    if (discountPercent > 0) {
        discountEl.className = 'text-2xl font-bold text-green-600';
    } else if (discountPercent < 0) {
        discountEl.className = 'text-2xl font-bold text-red-600';
    } else {
        discountEl.className = 'text-2xl font-bold text-gray-600';
    }

    // Build adjusted prices table
    // Sort cart items by category display order
    const sortedCart = [...cart].sort((a, b) => {
        const catA = (a.category || getCategoryFromLotName(a.lot_name)).toUpperCase();
        const catB = (b.category || getCategoryFromLotName(b.lot_name)).toUpperCase();
        const orderA = categoryOrderMap[catA] || 9999;
        const orderB = categoryOrderMap[catB] || 9999;
        return orderA - orderB;
    });

    let html = '';
    let totalOriginal = 0;
    let totalNew = 0;

    sortedCart.forEach(item => {
        const originalPrice = getDisplayPrice(item);
        const newPrice = originalPrice * coefficient;
        const itemOriginalTotal = originalPrice * item.quantity;
        const itemNewTotal = newPrice * item.quantity;

        totalOriginal += itemOriginalTotal;
        totalNew += itemNewTotal;

        html += `
            <tr>
                <td class="px-3 py-2 font-mono">${escapeHtml(item.lot_name)}</td>
                <td class="px-3 py-2 text-right">${item.quantity}</td>
                <td class="px-3 py-2 text-right text-gray-500">${formatMoney(originalPrice)}</td>
                <td class="px-3 py-2 text-right text-green-600">${formatMoney(newPrice)}</td>
                <td class="px-3 py-2 text-right">${formatMoney(itemNewTotal)}</td>
            </tr>
        `;
    });

    document.getElementById('adjusted-prices-body').innerHTML = html;
    document.getElementById('adjusted-total-original').textContent = formatMoney(totalOriginal);
    document.getElementById('adjusted-total-new').textContent = formatMoney(totalNew);
    document.getElementById('adjusted-total-final').textContent = formatMoney(totalNew);
    document.getElementById('adjusted-prices').classList.remove('hidden');
}

function clearCustomPrice() {
    document.getElementById('custom-price-input').value = '';
    document.getElementById('adjusted-prices').classList.add('hidden');
    document.getElementById('discount-info').classList.add('hidden');
    triggerAutoSave();
}

async function exportCSVWithCustomPrice() {
    if (cart.length === 0) return;
    if (priceLevelsRefreshTimer) {
        clearTimeout(priceLevelsRefreshTimer);
        priceLevelsRefreshTimer = null;
    }
    await refreshPriceLevels({ force: true, noCache: true });

    const customPrice = parseFloat(document.getElementById('custom-price-input').value) || 0;
    const originalTotal = cart.reduce((sum, item) => sum + (getDisplayPrice(item) * item.quantity), 0);

    if (customPrice <= 0 || originalTotal <= 0) {
        showToast('Введите целевую цену', 'error');
        return;
    }

    const coefficient = customPrice / originalTotal;

    // Create adjusted cart
    const adjustedCart = cart.map(item => ({
        ...item,
        unit_price: parseFloat((getDisplayPrice(item) * coefficient).toFixed(2))
    }));

    try {
        const resp = await fetch('/api/export/csv', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({items: adjustedCart, name: configName, project_uuid: projectUUID})
        });

        const blob = await resp.blob();
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = getFilenameFromResponse(resp) || (configName || 'config') + '.csv';
        a.click();
        window.URL.revokeObjectURL(url);
    } catch(e) {
        showToast('Ошибка экспорта', 'error');
    }
}

async function refreshPrices() {
    // RBAC disabled - no token check required
    if (!configUUID) return;
    if (disablePriceRefresh) {
        showToast('Обновление цен отключено в настройках', 'error');
        return;
    }

    try {
        const resp = await fetch('/api/configs/' + configUUID + '/refresh-prices', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            }
        });

        if (!resp.ok) {
            showToast('Ошибка обновления цен', 'error');
            return;
        }

        const config = await resp.json();

        // Update cart with new prices
        if (config.items && config.items.length > 0) {
            cart = config.items.map(item => ({
                lot_name: item.lot_name,
                quantity: item.quantity,
                unit_price: item.unit_price,
                estimate_price: item.unit_price,
                warehouse_price: null,
                competitor_price: null,
                description: item.description || '',
                category: item.category || getCategoryFromLotName(item.lot_name)
            }));
        }

        // Update price update date
        if (config.price_updated_at) {
            updatePriceUpdateDate(config.price_updated_at);
        }
        if (config.pricelist_id) {
            if (selectedPricelistIds.estimate) {
                selectedPricelistIds.estimate = config.pricelist_id;
            } else {
                resolvedAutoPricelistIds.estimate = Number(config.pricelist_id);
            }
            if (!activePricelistsBySource.estimate.some(opt => Number(opt.id) === Number(config.pricelist_id))) {
                await loadActivePricelists();
            }
            syncPriceSettingsControls();
            renderPricelistSettingsSummary();
            if (selectedPricelistIds.estimate) {
                persistLocalPriceSettings();
            }
        }

        // Re-render UI
        await refreshPriceLevels();
        renderTab();
        updateCartUI();

        showToast('Цены обновлены', 'success');
    } catch(e) {
        showToast('Ошибка обновления цен', 'error');
    }
}

function updatePriceUpdateDate(dateStr) {
    if (!dateStr) {
        document.getElementById('price-update-date').textContent = '';
        return;
    }

    const date = new Date(dateStr);
    const now = new Date();
    const diffMs = now - date;
    const diffMins = Math.floor(diffMs / 60000);
    const diffHours = Math.floor(diffMs / 3600000);
    const diffDays = Math.floor(diffMs / 86400000);

    let timeAgo;
    if (diffMins < 1) {
        timeAgo = 'только что';
    } else if (diffMins < 60) {
        timeAgo = diffMins + ' мин. назад';
    } else if (diffHours < 24) {
        timeAgo = diffHours + ' ч. назад';
    } else if (diffDays < 7) {
        timeAgo = diffDays + ' дн. назад';
    } else {
        timeAgo = date.toLocaleDateString('ru-RU');
    }

    document.getElementById('price-update-date').textContent = 'Обновлено: ' + timeAgo;
}

</script>
{{end}}

{{template "base" .}}
