/* ═══════════════════════════════════════════════════════
   CRIME SCENE — Criminal Case Archive
   SPA Engine with hash routing & PDF.js viewer
   ═══════════════════════════════════════════════════════ */

(function () {
    'use strict';

    const ROOT = document.getElementById('crimesceneRoot');
    const PDF_BASE = '/crimescene/docs';
    let DATA = null;

    // ─── Data Loading ────────────────────────────────────
    async function loadData() {
        try {
            const r = await fetch('/api/crimescene');
            if (!r.ok) throw new Error(`HTTP ${r.status}`);
            DATA = await r.json();
        } catch (e) {
            console.error('CRIMESCENE: data load failed', e);
            ROOT.innerHTML = `<div class="prop-empty">
                <div class="prop-empty-icon">⚠️</div>
                <div class="prop-empty-title">CASE DATABASE OFFLINE</div>
                <div class="prop-empty-text">Failed to load criminal case index. ${e.message}</div>
            </div>`;
        }
    }

    // ─── Helpers ──────────────────────────────────────────
    function countDocuments(obj) {
        if (!obj) return 0;
        let count = 0;
        if (Array.isArray(obj.documents)) count += obj.documents.length;
        if (Array.isArray(obj.subcollections)) {
            count += obj.subcollections.reduce((s, sc) => s + (sc.documents ? sc.documents.length : 0), 0);
        }
        if (Array.isArray(obj.collections)) {
            count += obj.collections.reduce((s, c) => s + countDocuments(c), 0);
        }
        if (Array.isArray(obj.countries)) {
            count += obj.countries.reduce((s, cn) => s + countDocuments(cn), 0);
        }
        return count;
    }

    function countCollections(obj) {
        if (!obj) return 0;
        if (Array.isArray(obj.collections)) return obj.collections.length;
        if (Array.isArray(obj.countries)) {
            return obj.countries.reduce((s, cn) => s + (cn.collections ? cn.collections.length : 0), 0);
        }
        return 0;
    }

    function countCountries(cat) {
        return cat.countries ? cat.countries.length : 0;
    }

    function totalStats() {
        if (!DATA || !DATA.categories) return { cats: 0, countries: 0, collections: 0, docs: 0 };
        let countries = new Set();
        let collections = 0;
        let docs = 0;
        DATA.categories.forEach(cat => {
            (cat.countries || []).forEach(cn => {
                countries.add(cn.code);
                (cn.collections || []).forEach(col => {
                    collections++;
                    docs += (col.documents || []).length;
                    (col.subcollections || []).forEach(sc => {
                        docs += (sc.documents || []).length;
                    });
                });
            });
        });
        return { cats: DATA.categories.length, countries: countries.size, collections, docs };
    }

    function findCategory(id) {
        return DATA.categories.find(c => c.id === id);
    }

    function findCountry(cat, code) {
        return (cat.countries || []).find(c => c.code === code);
    }

    function findCollection(country, colId) {
        return (country.collections || []).find(c => c.id === colId);
    }

    function findDocument(collection, docId) {
        return (collection.documents || []).find(d => d.id === docId);
    }

    function esc(s) {
        const d = document.createElement('div');
        d.textContent = s || '';
        return d.innerHTML;
    }

    // ─── Breadcrumb Builder ───────────────────────────────
    function breadcrumb(parts) {
        // parts: [{label, hash?}, ...]
        let html = '<div class="prop-breadcrumb">';
        parts.forEach((p, i) => {
            if (i > 0) html += '<span class="sep">/</span>';
            if (p.hash) {
                html += `<a onclick="location.hash='${p.hash}'">${esc(p.label)}</a>`;
            } else {
                html += `<span class="current">${esc(p.label)}</span>`;
            }
        });
        html += '</div>';
        return html;
    }

    // ─── View: Categories (Home) ──────────────────────────
    function renderCategories() {
        const stats = totalStats();
        let html = '';

        html += `<div class="prop-classification">CRIMINAL CASE ARCHIVE // ACCESS GRANTED</div>`;

        html += `<div class="prop-stats-bar">
            <div class="prop-stat"><span class="prop-stat-label">CATEGORIES</span><span class="prop-stat-value">${stats.cats}</span></div>
            <div class="prop-stat"><span class="prop-stat-label">COUNTRIES</span><span class="prop-stat-value amber">${stats.countries}</span></div>
            <div class="prop-stat"><span class="prop-stat-label">COLLECTIONS</span><span class="prop-stat-value">${stats.collections}</span></div>
            <div class="prop-stat"><span class="prop-stat-label">DOCUMENTS</span><span class="prop-stat-value red">${stats.docs}</span></div>
        </div>`;

        html += breadcrumb([{ label: 'CRIME SCENE' }]);

        // ─── Global Search Bar ────────────────────────────────
        html += `<div class="ur-search-container">
            <div class="ur-search-bar">
                <span class="ur-search-icon">⌕</span>
                <input type="text" id="csSearchInput" class="ur-search-input" placeholder="SEARCH CRIMINAL ARCHIVES..." autocomplete="off" spellcheck="false">
                <span class="ur-search-clear" id="csSearchClear" style="display:none;">✕</span>
            </div>
            <div class="ur-search-status" id="csSearchStatus"></div>
            <div class="ur-search-results" id="csSearchResults"></div>
        </div>`;

        html += '<div id="csCategorySection">';
        html += '<div class="prop-section-label">SELECT CATEGORY</div>';

        DATA.categories.forEach(cat => {
            const nCountries = countCountries(cat);
            const nDocs = countDocuments(cat);
            const nCol = countCollections(cat);
            html += `<div class="prop-card" onclick="location.hash='category/${cat.id}'">
                <div class="prop-card-header">
                    <div class="prop-card-name">${esc(cat.name)}</div>
                    <div class="prop-card-icon">${cat.icon || '📁'}</div>
                </div>
                <div class="prop-card-desc">${esc(cat.description)}</div>
                <div class="prop-card-meta">
                    <span>${nCountries} COUNTR${nCountries === 1 ? 'Y' : 'IES'} · ${nCol} COLLECTION${nCol === 1 ? '' : 'S'}</span>
                    <span class="prop-card-count">${nDocs} DOC${nDocs === 1 ? '' : 'S'}</span>
                </div>
            </div>`;
        });

        html += '</div>';
        html += '</div>'; // close #csCategorySection
        ROOT.innerHTML = html;

        // ─── Bind search events ────────────────────────────────
        initSearchListeners();
    }

    // ─── View: Category (Country Selector + Collections) ──
    function renderCategory(catId, activeCountry) {
        const cat = findCategory(catId);
        if (!cat) return renderNotFound('Category not found');

        let html = '';
        html += `<div class="prop-classification">CASE FILE // ${esc(cat.name)} // ACCESS GRANTED</div>`;

        html += breadcrumb([
            { label: 'CRIME SCENE', hash: '' },
            { label: cat.name }
        ]);

        // Country selector
        if (cat.countries && cat.countries.length > 0) {
            html += '<div class="prop-section-label">SELECT COUNTRY</div>';
            html += '<div class="prop-country-grid">';
            cat.countries.forEach(cn => {
                const nCol = cn.collections ? cn.collections.length : 0;
                const isActive = activeCountry === cn.code;
                html += `<div class="prop-country-btn${isActive ? ' active' : ''}" onclick="location.hash='country/${catId}/${cn.code}'">
                    <span class="prop-country-flag">${cn.flag || ''}</span>
                    <span class="prop-country-label">${esc(cn.name)}</span>
                    <span class="prop-country-count">[${nCol}]</span>
                </div>`;
            });
            html += '</div>';

            // If a country is selected, show its collections
            if (activeCountry) {
                const country = findCountry(cat, activeCountry);
                if (country) {
                    html += renderCountryCollections(cat, country);
                }
            } else {
                // Show all collections from all countries
                html += '<div class="prop-section-label">ALL COLLECTIONS</div>';
                let hasCols = false;
                cat.countries.forEach(cn => {
                    if (cn.collections && cn.collections.length > 0) {
                        hasCols = true;
                    }
                });
                if (hasCols) {
                    html += '<div class="prop-collection-grid">';
                    cat.countries.forEach(cn => {
                        (cn.collections || []).forEach(col => {
                            html += renderCollectionCard(catId, cn.code, col, cn.flag);
                        });
                    });
                    html += '</div>';
                } else {
                    html += renderEmpty('NO COLLECTIONS YET', 'Collections will appear here once documents are added to this category.');
                }
            }
        } else {
            html += renderEmpty('NO COUNTRIES REGISTERED', 'No country data available for this category yet.');
        }

        ROOT.innerHTML = html;
    }

    function renderCountryCollections(cat, country) {
        let html = '';
        html += `<div class="prop-section-label">${country.flag || ''} ${esc(country.name)} — COLLECTIONS</div>`;

        if (country.collections && country.collections.length > 0) {
            html += '<div class="prop-collection-grid">';
            country.collections.forEach(col => {
                html += renderCollectionCard(cat.id, country.code, col);
            });
            html += '</div>';
        } else {
            html += renderEmpty('NO COLLECTIONS', 'No document collections available for this country yet.');
        }
        return html;
    }

    function renderCollectionCard(catId, countryCode, col, flag) {
        let nDocs = col.documents ? col.documents.length : 0;
        if (col.subcollections) nDocs += col.subcollections.reduce((s, sc) => s + (sc.documents ? sc.documents.length : 0), 0);
        return `<div class="prop-collection-card" onclick="location.hash='collection/${catId}/${countryCode}/${col.id}'">
            <div class="prop-collection-name">${flag ? flag + ' ' : ''}${esc(col.name)}</div>
            <div class="prop-collection-year">${esc(col.year)}</div>
            <div class="prop-collection-desc">${esc(col.description)}</div>
            <div class="prop-collection-footer">
                <span class="prop-collection-source">SRC: ${esc(col.source)}</span>
                <span class="prop-collection-docs">${nDocs} DOCUMENT${nDocs === 1 ? '' : 'S'}</span>
            </div>
        </div>`;
    }

    // ─── View: Collection (Documents) ─────────────────────
    function renderCollection(catId, countryCode, colId) {
        const cat = findCategory(catId);
        if (!cat) return renderNotFound('Category not found');
        const country = findCountry(cat, countryCode);
        if (!country) return renderNotFound('Country not found');
        const col = findCollection(country, colId);
        if (!col) return renderNotFound('Collection not found');

        let html = '';
        html += `<div class="prop-classification">CASE FILE // ${esc(col.name).toUpperCase()} // ACCESS GRANTED</div>`;

        html += breadcrumb([
            { label: 'CRIME SCENE', hash: '' },
            { label: cat.name, hash: `category/${catId}` },
            { label: `${country.flag || ''} ${country.name}`, hash: `country/${catId}/${countryCode}` },
            { label: col.name }
        ]);

        // Detail header
        html += `<div class="prop-detail-header">
            <div class="prop-detail-title">${esc(col.name)}</div>
            <div class="prop-detail-year">${esc(col.year)}</div>
            <div class="prop-detail-desc">${esc(col.description)}</div>
            <div class="prop-detail-source">SOURCE: <span>${esc(col.source)}</span></div>
        </div>`;

        // Documents
        if (col.documents && col.documents.length > 0) {
            html += `<div class="prop-section-label">DOCUMENTS [${col.documents.length}]</div>`;
            html += '<div class="prop-doc-list">';
            col.documents.forEach(doc => {
                html += `<div class="prop-doc-item" onclick="location.hash='doc/${catId}/${countryCode}/${colId}/${doc.id}'">
                    <div class="prop-doc-icon">📄</div>
                    <div class="prop-doc-info">
                        <div class="prop-doc-title">${esc(doc.title)}</div>
                        <div class="prop-doc-desc">${esc(doc.description)}</div>
                        <div class="prop-doc-meta">
                            <span>PAGES: ${doc.pages || '?'}</span>
                            <span>RELEASED: ${esc(doc.date_released || 'UNKNOWN')}</span>
                            <span>${esc(doc.filename)}</span>
                        </div>
                    </div>
                    <div class="prop-doc-badge">VIEW</div>
                </div>`;
            });
            html += '</div>';
        } else if (!col.subcollections || col.subcollections.length === 0) {
            html += renderEmpty('NO DOCUMENTS UPLOADED YET', 'This collection has been catalogued but documents have not yet been uploaded to the archive.');
        }

        // Subcollections (accordion sections)
        if (col.subcollections && col.subcollections.length > 0) {
            html += `<div class="prop-section-label">SUB-COLLECTIONS [${col.subcollections.length}]</div>`;
            col.subcollections.forEach(sc => {
                const nScDocs = sc.documents ? sc.documents.length : 0;
                html += `<div class="prop-subcollection collapsed">`;
                html += `<div class="prop-subcollection-header" onclick="this.parentElement.classList.toggle('collapsed')">`;
                html += `<span class="prop-subcollection-toggle">▶</span>`;
                html += `<span class="prop-subcollection-name">${esc(sc.name)}</span>`;
                html += `<span class="prop-subcollection-count">[${nScDocs} DOC${nScDocs === 1 ? '' : 'S'}]</span>`;
                html += `</div>`;
                if (sc.description) {
                    html += `<div class="prop-subcollection-desc">${esc(sc.description)}</div>`;
                }
                if (sc.documents && sc.documents.length > 0) {
                    html += '<div class="prop-doc-list">';
                    sc.documents.forEach(doc => {
                        html += `<div class="prop-doc-item" onclick="location.hash='doc/${catId}/${countryCode}/${colId}/${sc.id}/${doc.id}'">`;
                        html += `<div class="prop-doc-icon">📄</div>`;
                        html += `<div class="prop-doc-info">`;
                        html += `<div class="prop-doc-title">${esc(doc.title)}</div>`;
                        html += `<div class="prop-doc-desc">${esc(doc.description)}</div>`;
                        html += `<div class="prop-doc-meta">`;
                        html += `<span>PAGES: ${doc.pages || '?'}</span>`;
                        html += `<span>RELEASED: ${esc(doc.date_released || 'UNKNOWN')}</span>`;
                        html += `<span>${esc(doc.filename)}</span>`;
                        html += `</div></div>`;
                        html += `<div class="prop-doc-badge">VIEW</div>`;
                        html += `</div>`;
                    });
                    html += '</div>';
                } else {
                    html += renderEmpty('NO DOCUMENTS YET', 'Documents for this sub-section have not yet been uploaded.');
                }
                html += '</div>';
            });
        }

        ROOT.innerHTML = html;
    }

    // ─── View: Document (PDF Viewer) ──────────────────────
    function renderDocument(catId, countryCode, colId, subOrDocId, maybeDocId) {
        const cat = findCategory(catId);
        if (!cat) return renderNotFound('Category not found');
        const country = findCountry(cat, countryCode);
        if (!country) return renderNotFound('Country not found');
        const col = findCollection(country, colId);
        if (!col) return renderNotFound('Collection not found');

        let doc, subcol, docId, pdfUrl;
        if (maybeDocId) {
            // 6-part route: subcollection doc
            const scId = subOrDocId;
            docId = maybeDocId;
            subcol = (col.subcollections || []).find(sc => sc.id === scId);
            if (!subcol) return renderNotFound('Sub-collection not found');
            doc = findDocument(subcol, docId);
            if (!doc) return renderNotFound('Document not found');
            pdfUrl = `${PDF_BASE}/${catId}/${countryCode}/${colId}/${scId}/${doc.filename}`;
        } else {
            // 5-part route: direct collection doc
            docId = subOrDocId;
            doc = findDocument(col, docId);
            if (!doc) return renderNotFound('Document not found');
            pdfUrl = `${PDF_BASE}/${catId}/${countryCode}/${colId}/${doc.filename}`;
        }

        let html = '';
        html += `<div class="prop-classification">CASE FILE // ${esc(doc.title).toUpperCase()}</div>`;

        const bcParts = [
            { label: 'CRIME SCENE', hash: '' },
            { label: cat.name, hash: `category/${catId}` },
            { label: `${country.flag || ''} ${country.name}`, hash: `country/${catId}/${countryCode}` },
            { label: col.name, hash: `collection/${catId}/${countryCode}/${colId}` }
        ];
        if (subcol) bcParts.push({ label: subcol.name });
        bcParts.push({ label: doc.title });
        html += breadcrumb(bcParts);

        // Viewer
        html += `<div class="prop-viewer-container" id="pdfViewerContainer">
            <div class="prop-viewer-toolbar">
                <div class="prop-viewer-title">${esc(doc.title)}</div>
                <div class="prop-viewer-controls">
                    <button class="prop-viewer-btn" id="pdfPrev" title="Previous page">◂ PREV</button>
                    <span class="prop-viewer-page-info" id="pdfPageInfo">— / —</span>
                    <button class="prop-viewer-btn" id="pdfNext" title="Next page">NEXT ▸</button>
                    <button class="prop-viewer-btn" id="pdfZoomOut" title="Zoom out">−</button>
                    <button class="prop-viewer-btn" id="pdfZoomIn" title="Zoom in">+</button>
                    <button class="prop-viewer-btn" id="pdfFit" title="Fit width">FIT</button>
                    <button class="prop-viewer-btn" id="pdfSearchToggle" title="Search (Ctrl+F)">🔍 FIND</button>
                    <a class="prop-viewer-download" href="${pdfUrl}" target="_blank" download>⬇ DOWNLOAD</a>
                </div>
            </div>
            <div class="prop-search-bar" id="pdfSearchBar">
                <div class="prop-search-inner">
                    <input type="text" class="prop-search-input" id="pdfSearchInput" placeholder="SEARCH DOCUMENT..." autocomplete="off" spellcheck="false">
                    <button class="prop-search-btn" id="pdfSearchCaseBtn" title="Case sensitive">Aa</button>
                    <span class="prop-search-info" id="pdfSearchInfo"></span>
                    <button class="prop-search-btn" id="pdfSearchPrev" title="Previous match (Shift+Enter)">▲</button>
                    <button class="prop-search-btn" id="pdfSearchNext" title="Next match (Enter)">▼</button>
                    <button class="prop-search-btn prop-search-close" id="pdfSearchClose" title="Close search (Esc)">✕</button>
                </div>
            </div>
            <div class="prop-viewer-canvas-wrap" id="pdfCanvasWrap">
                <div class="prop-viewer-loading" id="pdfLoading">RETRIEVING CASE FILE...</div>
            </div>
        </div>`;

        // Doc info
        html += `<div class="prop-detail-header">
            <div class="prop-detail-title">${esc(doc.title)}</div>
            <div class="prop-detail-desc">${esc(doc.description)}</div>
            <div class="prop-doc-meta" style="margin-top:0.5rem">
                <span>PAGES: ${doc.pages || '?'}</span>
                <span>RELEASED: ${esc(doc.date_released || 'UNKNOWN')}</span>
                <span>FILE: ${esc(doc.filename)}</span>
            </div>
        </div>`;

        ROOT.innerHTML = html;

        // Init PDF viewer
        initPdfViewer(pdfUrl);
    }

    // ─── PDF.js Viewer ───────────────────────────────────
    let pdfDoc = null;
    let pdfPage = 1;
    let pdfScale = 1.5;
    let pdfRendering = false;
    let pdfPending = null;
    let pdfAllText = [];
    let pdfSearchActive = false;
    let pdfSearchQuery = '';
    let pdfSearchCaseSensitive = false;
    let pdfSearchMatches = [];
    let pdfSearchCurrentIdx = -1;


    async function initPdfViewer(url) {
        const canvasWrap = document.getElementById('pdfCanvasWrap');
        const loadingEl = document.getElementById('pdfLoading');
        const pageInfo = document.getElementById('pdfPageInfo');

        // Check if pdf.js is loaded
        if (typeof pdfjsLib === 'undefined') {
            canvasWrap.innerHTML = `<div class="prop-viewer-error">
                <div class="prop-viewer-error-icon">⚠️</div>
                <div class="prop-viewer-error-text">PDF.js library not loaded</div>
                <a class="prop-viewer-download" href="${url}" target="_blank" download>⬇ DOWNLOAD PDF DIRECTLY</a>
            </div>`;
            return;
        }

        try {
            pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
            const loadingTask = pdfjsLib.getDocument(url);
            pdfDoc = await loadingTask.promise;
            pdfPage = 1;

            loadingEl.remove();

            // Create page container with canvas
            const pageContainer = document.createElement('div');
            pageContainer.id = 'pdfPageContainer';
            pageContainer.className = 'prop-page-container';
            const canvas = document.createElement('canvas');
            canvas.id = 'pdfCanvas';
            pageContainer.appendChild(canvas);
            canvasWrap.appendChild(pageContainer);

            updatePageInfo();
            renderPdfPage();

            // Controls
            document.getElementById('pdfPrev').addEventListener('click', () => {
                if (pdfPage <= 1) return;
                pdfPage--;
                queueRender();
            });
            document.getElementById('pdfNext').addEventListener('click', () => {
                if (pdfPage >= pdfDoc.numPages) return;
                pdfPage++;
                queueRender();
            });
            document.getElementById('pdfZoomIn').addEventListener('click', () => {
                pdfScale = Math.min(pdfScale + 0.25, 4);
                queueRender();
            });
            document.getElementById('pdfZoomOut').addEventListener('click', () => {
                pdfScale = Math.max(pdfScale - 0.25, 0.5);
                queueRender();
            });
            document.getElementById('pdfFit').addEventListener('click', () => {
                pdfScale = 1.5;
                queueRender();
            });

            // Extract all page text for search
            extractAllText();

            // Initialize search controls
            initSearchControls();
        } catch (e) {
            console.error('PDF load failed:', e);
            canvasWrap.innerHTML = `<div class="prop-viewer-error">
                <div class="prop-viewer-error-icon">⚠️</div>
                <div class="prop-viewer-error-text">DOCUMENT LOAD FAILED — ${esc(e.message || 'File may not exist yet')}</div>
                <a class="prop-viewer-download" href="${url}" target="_blank" download>⬇ ATTEMPT DIRECT DOWNLOAD</a>
            </div>`;
        }
    }

    function updatePageInfo() {
        const el = document.getElementById('pdfPageInfo');
        if (el && pdfDoc) {
            el.textContent = `${pdfPage} / ${pdfDoc.numPages}`;
        }
    }

    async function renderPdfPage() {
        if (!pdfDoc) return;
        pdfRendering = true;

        const page = await pdfDoc.getPage(pdfPage);
        const viewport = page.getViewport({ scale: pdfScale });
        const canvas = document.getElementById('pdfCanvas');
        if (!canvas) { pdfRendering = false; return; }
        const ctx = canvas.getContext('2d');
        canvas.height = viewport.height;
        canvas.width = viewport.width;

        // Update page container size to match viewport
        const pageContainer = document.getElementById('pdfPageContainer');
        if (pageContainer) {
            pageContainer.style.width = viewport.width + 'px';
            pageContainer.style.height = viewport.height + 'px';
        }

        await page.render({ canvasContext: ctx, viewport: viewport }).promise;

        // Render text layer for search and text selection
        await renderPageTextLayer(page, viewport);

        // Highlight search matches on this page
        highlightCurrentPageMatches();

        pdfRendering = false;
        updatePageInfo();

        if (pdfPending !== null) {
            renderPdfPage();
            pdfPending = null;
        }
    }

    function queueRender() {
        if (pdfRendering) {
            pdfPending = pdfPage;
        } else {
            renderPdfPage();
        }
    }

    // ─── Text Layer Rendering ────────────────────────────
    async function renderPageTextLayer(page, viewport) {
        const existing = document.getElementById('pdfTextLayer');
        if (existing) existing.remove();

        const pageContainer = document.getElementById('pdfPageContainer');
        if (!pageContainer) return;

        const textContent = await page.getTextContent();
        const textLayerDiv = document.createElement('div');
        textLayerDiv.id = 'pdfTextLayer';
        textLayerDiv.className = 'prop-text-layer';
        textLayerDiv.style.width = viewport.width + 'px';
        textLayerDiv.style.height = viewport.height + 'px';

        // Try pdf.js built-in renderTextLayer, fallback to manual
        if (typeof pdfjsLib.renderTextLayer === 'function') {
            try {
                const task = pdfjsLib.renderTextLayer({
                    textContentSource: textContent,
                    container: textLayerDiv,
                    viewport: viewport,
                });
                await task.promise;
            } catch (err) {
                console.warn('renderTextLayer failed, using fallback:', err);
                buildTextLayerFallback(textContent, viewport, textLayerDiv);
            }
        } else {
            buildTextLayerFallback(textContent, viewport, textLayerDiv);
        }

        // Tag each span with its item index for search highlighting
        let itemIdx = 0;
        for (const child of textLayerDiv.childNodes) {
            if (child.nodeName === 'SPAN') {
                child.dataset.itemIdx = itemIdx;
                itemIdx++;
            }
        }

        pageContainer.appendChild(textLayerDiv);
    }

    function buildTextLayerFallback(textContent, viewport, container) {
        textContent.items.forEach((item, idx) => {
            if (!item.str) return;
            const span = document.createElement('span');
            span.textContent = item.str;
            const tx = item.transform;
            const fontHeight = Math.sqrt(tx[2] * tx[2] + tx[3] * tx[3]);
            const s = viewport.scale;
            span.style.fontSize = (fontHeight * s) + 'px';
            span.style.fontFamily = 'sans-serif';
            span.style.left = (tx[4] * s) + 'px';
            span.style.top = (viewport.height - tx[5] * s - fontHeight * s) + 'px';
            span.style.position = 'absolute';
            span.style.color = 'transparent';
            span.style.whiteSpace = 'pre';
            container.appendChild(span);
        });
    }

    // ─── Text Extraction for Search ──────────────────────
    async function extractAllText() {
        pdfAllText = [];
        if (!pdfDoc) return;
        for (let i = 1; i <= pdfDoc.numPages; i++) {
            const page = await pdfDoc.getPage(i);
            const tc = await page.getTextContent();
            let fullText = '';
            const itemOffsets = [];
            tc.items.forEach((item, idx) => {
                const start = fullText.length;
                fullText += item.str;
                itemOffsets.push({ start: start, end: fullText.length, itemIdx: idx });
                if (item.hasEOL) fullText += '\n';
            });
            pdfAllText.push({ pageNum: i, text: fullText, items: tc.items, itemOffsets: itemOffsets });
        }
        console.log('CRIMESCENE: Extracted text from ' + pdfAllText.length + ' pages');
    }

    // ─── Search Controls Init ────────────────────────────
    function initSearchControls() {
        const searchInput = document.getElementById('pdfSearchInput');
        const searchPrev = document.getElementById('pdfSearchPrev');
        const searchNext = document.getElementById('pdfSearchNext');
        const searchClose = document.getElementById('pdfSearchClose');
        const searchToggle = document.getElementById('pdfSearchToggle');
        const searchCaseBtn = document.getElementById('pdfSearchCaseBtn');
        if (!searchInput) return;

        searchToggle.addEventListener('click', () => toggleSearchBar());
        searchClose.addEventListener('click', () => closeSearchBar());

        let searchTimeout = null;
        searchInput.addEventListener('input', () => {
            clearTimeout(searchTimeout);
            searchTimeout = setTimeout(() => {
                pdfSearchQuery = searchInput.value;
                performSearch();
            }, 200);
        });

        searchInput.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault();
                if (e.shiftKey) prevMatch(); else nextMatch();
            }
            if (e.key === 'Escape') {
                e.preventDefault();
                closeSearchBar();
            }
        });

        searchPrev.addEventListener('click', () => prevMatch());
        searchNext.addEventListener('click', () => nextMatch());

        searchCaseBtn.addEventListener('click', () => {
            pdfSearchCaseSensitive = !pdfSearchCaseSensitive;
            searchCaseBtn.classList.toggle('active', pdfSearchCaseSensitive);
            performSearch();
        });

        // Ctrl+F shortcut — only when PDF viewer is present
        document.addEventListener('keydown', (e) => {
            if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
                if (document.getElementById('pdfViewerContainer')) {
                    e.preventDefault();
                    toggleSearchBar(true);
                }
            }
        });
    }

    function toggleSearchBar(forceOpen) {
        const searchBar = document.getElementById('pdfSearchBar');
        const searchInput = document.getElementById('pdfSearchInput');
        if (!searchBar) return;
        if (forceOpen || !pdfSearchActive) {
            searchBar.classList.add('active');
            pdfSearchActive = true;
            setTimeout(() => searchInput && searchInput.focus(), 50);
        } else {
            closeSearchBar();
        }
    }

    function closeSearchBar() {
        const searchBar = document.getElementById('pdfSearchBar');
        if (!searchBar) return;
        searchBar.classList.remove('active');
        pdfSearchActive = false;
        pdfSearchQuery = '';
        pdfSearchMatches = [];
        pdfSearchCurrentIdx = -1;
        const searchInput = document.getElementById('pdfSearchInput');
        if (searchInput) searchInput.value = '';
        updateSearchInfo();
        clearHighlights();
    }

    function performSearch() {
        pdfSearchMatches = [];
        pdfSearchCurrentIdx = -1;
        const query = pdfSearchQuery.trim();
        if (!query || query.length === 0) {
            updateSearchInfo();
            clearHighlights();
            return;
        }
        const cs = pdfSearchCaseSensitive;
        const q = cs ? query : query.toLowerCase();
        pdfAllText.forEach(pageData => {
            const text = cs ? pageData.text : pageData.text.toLowerCase();
            let pos = 0;
            while ((pos = text.indexOf(q, pos)) !== -1) {
                const matchStart = pos;
                const matchEnd = pos + q.length;
                const affectedItems = pageData.itemOffsets.filter(
                    io => io.end > matchStart && io.start < matchEnd
                );
                pdfSearchMatches.push({
                    page: pageData.pageNum,
                    charStart: matchStart,
                    charEnd: matchEnd,
                    itemIndices: affectedItems.map(io => io.itemIdx)
                });
                pos += 1;
            }
        });
        pdfSearchCurrentIdx = pdfSearchMatches.length > 0 ? 0 : -1;
        updateSearchInfo();
        if (pdfSearchMatches.length > 0) {
            goToMatch(0);
        } else {
            clearHighlights();
        }
    }

    function goToMatch(idx) {
        if (idx < 0 || idx >= pdfSearchMatches.length) return;
        pdfSearchCurrentIdx = idx;
        const match = pdfSearchMatches[idx];
        if (match.page !== pdfPage) {
            pdfPage = match.page;
            queueRender();
        } else {
            highlightCurrentPageMatches();
        }
        updateSearchInfo();
    }

    function nextMatch() {
        if (pdfSearchMatches.length === 0) return;
        goToMatch((pdfSearchCurrentIdx + 1) % pdfSearchMatches.length);
    }

    function prevMatch() {
        if (pdfSearchMatches.length === 0) return;
        goToMatch((pdfSearchCurrentIdx - 1 + pdfSearchMatches.length) % pdfSearchMatches.length);
    }

    function highlightCurrentPageMatches() {
        clearHighlights();
        const textLayer = document.getElementById('pdfTextLayer');
        if (!textLayer) return;
        const pageMatches = pdfSearchMatches.filter(m => m.page === pdfPage);
        if (pageMatches.length === 0) return;

        pageMatches.forEach(match => {
            const globalIdx = pdfSearchMatches.indexOf(match);
            const isActive = globalIdx === pdfSearchCurrentIdx;
            match.itemIndices.forEach(itemIdx => {
                const span = textLayer.querySelector('span[data-item-idx="' + itemIdx + '"]');
                if (span) {
                    span.classList.add('prop-search-hl');
                    if (isActive) {
                        span.classList.add('prop-search-hl-active');
                        const canvasWrap = document.getElementById('pdfCanvasWrap');
                        if (canvasWrap) {
                            const sr = span.getBoundingClientRect();
                            const wr = canvasWrap.getBoundingClientRect();
                            const offset = sr.top - wr.top - wr.height / 3;
                            canvasWrap.scrollBy({ top: offset, behavior: 'smooth' });
                        }
                    }
                }
            });
        });
    }

    function clearHighlights() {
        const tl = document.getElementById('pdfTextLayer');
        if (!tl) return;
        tl.querySelectorAll('.prop-search-hl').forEach(el => {
            el.classList.remove('prop-search-hl', 'prop-search-hl-active');
        });
    }

    function updateSearchInfo() {
        const info = document.getElementById('pdfSearchInfo');
        if (!info) return;
        if (pdfSearchMatches.length === 0) {
            if (pdfSearchQuery && pdfSearchQuery.trim().length > 0) {
                info.textContent = 'NO MATCHES';
                info.className = 'prop-search-info no-match';
            } else {
                info.textContent = '';
                info.className = 'prop-search-info';
            }
        } else {
            info.textContent = (pdfSearchCurrentIdx + 1) + ' OF ' + pdfSearchMatches.length;
            info.className = 'prop-search-info';
        }
    }


    // ─── Global Archive Search ─────────────────────────────
    let searchDebounceTimer = null;

    function initSearchListeners() {
        const input = document.getElementById('csSearchInput');
        const clearBtn = document.getElementById('csSearchClear');
        if (!input) return;

        input.addEventListener('input', () => {
            clearTimeout(searchDebounceTimer);
            const val = input.value.trim();
            clearBtn.style.display = val.length > 0 ? 'inline-block' : 'none';
            searchDebounceTimer = setTimeout(() => {
                globalArchiveSearch(val);
            }, 300);
        });

        clearBtn.addEventListener('click', () => {
            input.value = '';
            clearBtn.style.display = 'none';
            globalArchiveSearch('');
            input.focus();
        });

        input.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                input.value = '';
                clearBtn.style.display = 'none';
                globalArchiveSearch('');
            }
        });
    }

    function globalArchiveSearch(query) {
        const resultsEl = document.getElementById('csSearchResults');
        const statusEl = document.getElementById('csSearchStatus');
        const catSection = document.getElementById('csCategorySection');
        if (!resultsEl || !statusEl) return;

        if (!query || query.length === 0) {
            resultsEl.innerHTML = '';
            statusEl.innerHTML = '';
            if (catSection) catSection.style.display = '';
            return;
        }

        const q = query.toLowerCase();
        const results = [];

        if (!DATA || !DATA.categories) return;

        DATA.categories.forEach(cat => {
            (cat.countries || []).forEach(cn => {
                (cn.collections || []).forEach(col => {
                    (col.documents || []).forEach(doc => {
                        const fields = [
                            doc.title || '',
                            doc.description || '',
                            col.name || '',
                            cat.name || '',
                            cn.name || '',
                            doc.year ? String(doc.year) : '',
                            doc.classification || ''
                        ];
                        const combined = fields.join(' ').toLowerCase();
                        if (combined.includes(q)) {
                            results.push({
                                doc,
                                catName: cat.name,
                                catId: cat.id,
                                catIcon: cat.icon || '📁',
                                countryName: cn.name,
                                countryCode: cn.code,
                                countryFlag: cn.flag || '',
                                colName: col.name,
                                colId: col.id
                            });
                        }
                    });

                    // Index subcollection documents
                    (col.subcollections || []).forEach(sc => {
                        (sc.documents || []).forEach(doc => {
                            const scFields = [
                                doc.title || '',
                                doc.description || '',
                                sc.name || '',
                                col.name || '',
                                cat.name || '',
                                cn.name || '',
                                doc.year ? String(doc.year) : '',
                                doc.classification || ''
                            ];
                            const scCombined = scFields.join(' ').toLowerCase();
                            if (scCombined.includes(q)) {
                                results.push({
                                    doc,
                                    catName: cat.name,
                                    catId: cat.id,
                                    catIcon: cat.icon || '📁',
                                    countryName: cn.name,
                                    countryCode: cn.code,
                                    countryFlag: cn.flag || '',
                                    colName: sc.name,
                                    colId: col.id,
                                    subcolId: sc.id
                                });
                            }
                        });
                    });

                    // Also match collection-level (even if no docs match)
                    const colFields = [col.name || '', col.description || '', cat.name || '', cn.name || ''].join(' ').toLowerCase();
                    if (colFields.includes(q) && !results.find(r => r.colId === col.id && r.catId === cat.id && r.countryCode === cn.code && !r.doc.id)) {
                        // Check if we already added docs from this collection
                        const hasDocResults = results.some(r => r.colId === col.id && r.catId === cat.id && r.countryCode === cn.code);
                        if (!hasDocResults) {
                            results.push({
                                doc: { title: col.name, description: col.description || 'Collection in ' + cat.name, id: '__col__' },
                                catName: cat.name,
                                catId: cat.id,
                                catIcon: cat.icon || '📁',
                                countryName: cn.name,
                                countryCode: cn.code,
                                countryFlag: cn.flag || '',
                                colName: col.name,
                                colId: col.id,
                                isCollection: true
                            });
                        }
                    }
                });
            });
        });

        // Hide categories when searching
        if (catSection) catSection.style.display = 'none';

        if (results.length === 0) {
            statusEl.innerHTML = '<span class="ur-search-none">NO MATCHING CASE FILES // REFINE SEARCH PARAMETERS</span>';
            resultsEl.innerHTML = '';
            return;
        }

        statusEl.innerHTML = `<span class="ur-search-count">FOUND ${results.length} MATCHING CASE FILE${results.length === 1 ? '' : 'S'}</span>`;

        let html = '<div class="ur-results-grid">';
        results.forEach(r => {
            const title = highlightMatch(esc(r.doc.title || 'Untitled'), q);
            const desc = r.doc.description ? highlightMatch(esc(truncateText(r.doc.description, 160)), q) : '';
            let hash;
            if (r.isCollection) {
                hash = `country/${r.catId}/${r.countryCode}`;
            } else if (r.subcolId) {
                hash = `doc/${r.catId}/${r.countryCode}/${r.colId}/${r.subcolId}/${r.doc.id}`;
            } else {
                hash = `doc/${r.catId}/${r.countryCode}/${r.colId}/${r.doc.id}`;
            }

            html += `<div class="ur-result-card" onclick="location.hash='${hash}'">
                <div class="ur-result-header">
                    <span class="ur-result-icon">${r.catIcon}</span>
                    <span class="ur-result-title">${title}</span>
                </div>
                <div class="ur-result-meta">
                    <span class="ur-result-badge">${esc(r.catName)}</span>
                    <span class="ur-result-badge ur-result-badge-country">${r.countryFlag} ${esc(r.countryName)}</span>
                    <span class="ur-result-badge ur-result-badge-col">${esc(r.colName)}</span>
                </div>
                ${desc ? `<div class="ur-result-desc">${desc}</div>` : ''}
            </div>`;
        });
        html += '</div>';
        resultsEl.innerHTML = html;
    }

    function highlightMatch(text, query) {
        if (!query) return text;
        const regex = new RegExp('(' + query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')', 'gi');
        return text.replace(regex, '<mark class="ur-highlight">$1</mark>');
    }

    function truncateText(text, maxLen) {
        if (text.length <= maxLen) return text;
        return text.substring(0, maxLen).replace(/\s+\S*$/, '') + '...';
    }

    // ─── Not Found / Empty ────────────────────────────────
    function renderNotFound(msg) {
        ROOT.innerHTML = `<div class="prop-empty">
            <div class="prop-empty-icon">⚠️</div>
            <div class="prop-empty-title">${esc(msg)}</div>
            <div class="prop-empty-text">The requested case file could not be located in the criminal archive.</div>
        </div>
        <div style="text-align:center;margin-top:1rem">
            <a class="prop-back-btn" onclick="location.hash=''">◂ RETURN TO CASE INDEX</a>
        </div>`;
    }

    function renderEmpty(title, text) {
        return `<div class="prop-empty">
            <div class="prop-empty-icon">◎</div>
            <div class="prop-empty-title">${esc(title)}</div>
            <div class="prop-empty-text">${esc(text)}</div>
        </div>`;
    }

    // ─── Router ───────────────────────────────────────────
    function route() {
        if (!DATA) return;

        // Reset PDF state
        pdfDoc = null;
        pdfPage = 1;
        pdfRendering = false;
        pdfPending = null;
        pdfAllText = [];
        pdfSearchActive = false;
        pdfSearchQuery = '';
        pdfSearchMatches = [];
        pdfSearchCurrentIdx = -1;


        const hash = (location.hash || '').replace(/^#\/?/, '');
        const parts = hash.split('/').filter(Boolean);

        if (parts.length === 0 || hash === '') {
            renderCategories();
        } else if (parts[0] === 'category' && parts[1]) {
            renderCategory(parts[1], null);
        } else if (parts[0] === 'country' && parts[1] && parts[2]) {
            renderCategory(parts[1], parts[2]);
        } else if (parts[0] === 'collection' && parts.length >= 4) {
            renderCollection(parts[1], parts[2], parts[3]);
        } else if (parts[0] === 'doc' && parts.length >= 6) {
            renderDocument(parts[1], parts[2], parts[3], parts[4], parts[5]);
        } else if (parts[0] === 'doc' && parts.length >= 5) {
            renderDocument(parts[1], parts[2], parts[3], parts[4]);
        } else {
            renderNotFound('INVALID ROUTE');
        }

        // Scroll to top
        window.scrollTo({ top: 0, behavior: 'smooth' });
    }

    // ─── Init ─────────────────────────────────────────────
    async function init() {
        ROOT.innerHTML = '<div class="prop-loading">INITIALISING CRIME SCENE ARCHIVE...</div>';
        await loadData();
        if (DATA) {
            window.addEventListener('hashchange', route);
            route();
        }
    }

    // Wait for DOM
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();
