/**
 * ZDDC Classifier — target-tree pane (Classify & Copy mode).
 *
 * Renders the two orthogonal target trees the user maps files onto:
 *   - "By tracking number": folders that join with "-" into the tracking
 *     number; the leaf folder ("A (IFR)") is the revision+status.
 *   - "By transmittal": <party>/{received,issued}/<transmittal folder>.
 *
 * Structure here, placements in classify.js. Drag-and-drop assignment is wired
 * in source-dnd.js / phase 3; this module owns rendering + folder/bin CRUD and
 * shows the derived filename for each placed file.
 */
(function () {
    'use strict';

    var SLOTS = ['received', 'issued'];

    var els = {};
    var collapsed = {};            // nodeId -> true when collapsed (default expanded)
    var openForm = null;           // { partyId, slot } when a bin form is open
    var initialized = false;
    var currentTab = 'tracking';   // 'tracking' | 'transmittal' — the active axis

    function init() {
        if (initialized) return;
        initialized = true;
        els = {
            trackingTab: document.getElementById('trackingTab'),
            transmittalTab: document.getElementById('transmittalTab'),
            trackingPanel: document.getElementById('trackingPanel'),
            transmittalPanel: document.getElementById('transmittalPanel'),
            trackingTree: document.getElementById('trackingTree'),
            transmittalTree: document.getElementById('transmittalTree'),
            addTrackingRootBtn: document.getElementById('addTrackingRootBtn'),
            addPartyBtn: document.getElementById('addPartyBtn'),
            stats: document.getElementById('classifyStats'),
        };

        els.trackingTab.addEventListener('click', function () { showTab('tracking'); });
        els.transmittalTab.addEventListener('click', function () { showTab('transmittal'); });
        els.addTrackingRootBtn.addEventListener('click', function () {
            var name = prompt('Root folder name (a tracking-number segment, e.g. "ACME-PROJ").\n'
                + 'Brace patterns expand: BMB-{PM,EL}-{0001-0002,0005}_A (IFR)', '');
            addFoldersFromPattern(null, name);
        });
        els.addPartyBtn.addEventListener('click', function () {
            var name = prompt('Party name (also the transmittal-number prefix):', '');
            if (name && name.trim()) C().addParty(name.trim());
        });

        els.trackingTree.addEventListener('click', onTrackingClick);
        els.transmittalTree.addEventListener('click', onTransmittalClick);
        els.trackingTree.addEventListener('change', onFileNameChange);
        els.transmittalTree.addEventListener('change', onFileNameChange);

        setupDropZone(els.trackingTree, 'tracking');
        setupDropZone(els.transmittalTree, 'transmittal');

        C().on(render);
        if (window.app.modules.store && window.app.modules.store.on) {
            window.app.modules.store.on('files', render);
        }
        render();
    }

    function C() { return window.app.modules.classify; }
    // Every scanned source file (classify mode reads the left tree, not the
    // selection-scoped grid). Lazy folders contribute their files once scanned.
    function allFiles() {
        var out = [];
        (function walk(nodes) {
            (nodes || []).forEach(function (n) {
                (n.files || []).forEach(function (f) { out.push(f); });
                walk(n.children);
            });
        })(window.app.folderTree || []);
        return out;
    }
    // One pass: group files by the node they're placed in, per axis.
    function buildPlaced(files) {
        var c = C(), byT = {}, byX = {};
        files.forEach(function (f) {
            var a = c.getAssignment(c.srcKeyForFile(f));
            if (!a) return;
            if (a.trackingNodeId) (byT[a.trackingNodeId] = byT[a.trackingNodeId] || []).push(f);
            if (a.transmittalNodeId) (byX[a.transmittalNodeId] = byX[a.transmittalNodeId] || []).push(f);
        });
        return { tracking: byT, transmittal: byX };
    }

    function showTab(which) {
        var t = which === 'transmittal';
        currentTab = t ? 'transmittal' : 'tracking';
        els.trackingTab.classList.toggle('active', !t);
        els.transmittalTab.classList.toggle('active', t);
        els.trackingPanel.hidden = t;
        els.transmittalPanel.hidden = !t;
        // The "Hide Assigned" filter on the source tree is per-axis, so the
        // visible set changes with the active tab — re-render the left tree.
        if (window.app.modules.tree && window.app.modules.tree.render) window.app.modules.tree.render();
    }
    function activeAxis() { return currentTab === 'transmittal' ? 'transmittal' : 'tracking'; }

    // Expand a brace pattern into folder names and create them (confirming a
    // multi-create first). parentId null = root folders. See expandFolderPattern.
    function addFoldersFromPattern(parentId, raw) {
        if (!raw || !raw.trim()) return;
        var names = C().expandFolderPattern(raw);
        if (!names.length) return;
        if (names.length > 1) {
            var shown = names.slice(0, 8).join('\n');
            if (names.length > 8) shown += '\n…and ' + (names.length - 8) + ' more';
            if (!confirm('Create ' + names.length + ' folders?\n\n' + shown)) return;
        }
        // Each expanded name is parsed into nested tracking levels (split on
        // "-", final "_" splits the leaf rev), reusing shared ancestors.
        names.forEach(function (nm) { C().addTrackingPath(parentId, C().parseFolderLevels(nm)); });
    }

    // ── render ───────────────────────────────────────────────────────────────
    function render() {
        if (!initialized || !C().isEnabled()) return;
        var files = allFiles();
        var placed = buildPlaced(files);
        renderTrackingInto(els.trackingTree, C().getTrackingTree(), placed.tracking);
        renderTransmittalInto(els.transmittalTree, C().getTransmittalTree(), placed.transmittal);
        renderStats(files);
    }

    function renderStats(files) {
        var s = C().stats(files);
        if (els.stats) {
            els.stats.textContent = s.done + ' done · ' + s.partial + ' in progress · '
                + s.none + ' unassigned · ' + s.excluded + ' excluded';
        }
        var copyBtn = document.getElementById('copyOutputBtn');
        if (copyBtn) {
            copyBtn.disabled = s.done === 0;
            copyBtn.textContent = s.done ? ('Copy ' + s.done + '…') : 'Copy…';
        }
    }

    function el(tag, cls, text) {
        var e = document.createElement(tag);
        if (cls) e.className = cls;
        if (text != null) e.textContent = text;
        return e;
    }

    function nodeActions(extra) {
        var wrap = el('span', 'tnode__actions');
        (extra || []).forEach(function (a) {
            var b = el('button', 'tnode__act', a.label);
            b.dataset.act = a.act;
            b.title = a.title || '';
            wrap.appendChild(b);
        });
        return wrap;
    }

    function fileList(files) {
        var box = el('div', 'tnode__files');
        files.forEach(function (f) {
            var d = C().deriveTarget(f);
            var row = el('div', 'tfile' + (d.errors.length ? ' tfile--err' : ''));
            row.dataset.key = d.key;
            var orig = el('span', 'tfile__orig', f.originalFilename + (f.extension ? '.' + f.extension : ''));
            orig.title = 'Click to preview';
            row.appendChild(orig);
            row.appendChild(el('span', 'tfile__arrow', '→'));
            // Editable derived filename — edit it to re-file the item.
            var name = el('input', 'tfile__name' + (d.errors.length ? ' tfile__name--err' : ''));
            name.type = 'text';
            name.value = d.filename || '';
            name.placeholder = '(incomplete)';
            name.title = d.errors.length ? d.errors.join('; ') : 'Edit this ZDDC filename to re-file the item';
            row.appendChild(name);
            box.appendChild(row);
        });
        return box;
    }

    // ── name filter (the autofilter box above the target trees) ────────────
    var rfTerms = [];
    function setNameFilter(q) {
        rfTerms = String(q || '').trim().toLowerCase().split(/\s+/).filter(Boolean);
        render();
    }
    function rfActive() { return rfTerms.length > 0; }
    function rfHit(text) {
        if (!rfTerms.length) return true;
        var t = String(text || '').toLowerCase();
        for (var i = 0; i < rfTerms.length; i++) { if (t.indexOf(rfTerms[i]) === -1) return false; }
        return true;
    }
    // A placed-file row matches on its original name or its derived ZDDC name.
    function fileRowMatches(f) {
        var orig = f.originalFilename + (f.extension ? '.' + f.extension : '');
        return rfHit(orig) || rfHit(C().deriveTarget(f).filename || '');
    }

    // Tracking tree (recursive, filter-aware — a match reveals its whole path).
    function renderTrackingInto(container, nodes, placedMap) {
        container.textContent = '';
        if (!nodes.length) {
            container.appendChild(el('div', 'target-empty', 'No tracking folders yet — “+ Root folder” to start.'));
            return;
        }
        nodes.forEach(function (n) { var e = trackingNode(n, placedMap, false); if (e) container.appendChild(e); });
        if (rfActive() && !container.children.length) {
            container.appendChild(el('div', 'target-empty', 'No matches in the tracking tree.'));
        }
    }
    function trackingNode(n, placedMap, ancMatched) {
        var matched = ancMatched || rfHit(n.name);
        var isLeaf = (n.children || []).length === 0;
        var expanded = !collapsed[n.id] || rfActive();   // auto-expand to reveal matches
        var childEls = [];
        if (expanded || rfActive()) {
            (n.children || []).forEach(function (c) { var ce = trackingNode(c, placedMap, matched); if (ce) childEls.push(ce); });
        }
        var placed = placedMap[n.id] || [];
        var shownFiles = (rfActive() && !matched) ? placed.filter(fileRowMatches) : placed;
        if (rfActive() && !matched && !childEls.length && !shownFiles.length) return null;

        var wrap = el('div', 'tnode' + (isLeaf ? ' tnode--leaf' : ''));
        wrap.dataset.id = n.id;
        var row = el('div', 'tnode__row');
        var toggle = el('button', 'tnode__toggle', isLeaf ? '·' : (expanded ? '▾' : '▸'));
        if (!isLeaf) toggle.dataset.act = 'toggle';
        row.appendChild(toggle);
        row.appendChild(el('span', 'tnode__name', n.name));
        if (placed.length) row.appendChild(el('span', 'tnode__badge', String(placed.length)));
        row.appendChild(nodeActions([
            { act: 'add', label: '＋', title: 'Add child folder' },
            { act: 'rename', label: '✎', title: 'Rename' },
            { act: 'del', label: '🗑', title: 'Delete' },
        ]));
        wrap.appendChild(row);
        if (shownFiles.length) wrap.appendChild(fileList(shownFiles));
        if (!isLeaf && expanded && childEls.length) {
            var kids = el('div', 'tnode__children');
            childEls.forEach(function (ce) { kids.appendChild(ce); });
            wrap.appendChild(kids);
        }
        return wrap;
    }

    // Transmittal tree
    function renderTransmittalInto(container, parties, placedMap) {
        container.textContent = '';
        if (!parties.length) {
            container.appendChild(el('div', 'target-empty', 'No parties yet — “+ Party” to start.'));
            return;
        }
        parties.forEach(function (p) { var e = partyNode(p, placedMap); if (e) container.appendChild(e); });
        if (rfActive() && !container.children.length) {
            container.appendChild(el('div', 'target-empty', 'No matches in the transmittal tree.'));
        }
    }
    function partyNode(party, placedMap) {
        var partyMatch = rfHit(party.name);
        var slotEls = [], anyBin = false;
        SLOTS.forEach(function (slot) {
            var slotNode = (party.children || []).filter(function (s) { return s.slot === slot; })[0];
            var sw = el('div', 'tslot');
            sw.dataset.party = party.id;
            sw.dataset.slot = slot;
            var sr = el('div', 'tslot__row');
            sr.appendChild(el('span', 'tslot__name', slot));
            var addBtn = el('button', 'tnode__act', '+ Transmittal');
            addBtn.dataset.act = 'addbin';
            sr.appendChild(addBtn);
            sw.appendChild(sr);

            if (openForm && openForm.partyId === party.id && openForm.slot === slot) {
                sw.appendChild(binForm(party.id, slot));
            }
            (slotNode ? slotNode.children : []).forEach(function (bin) {
                var be = binNode(bin, placedMap, partyMatch);
                if (be) { sw.appendChild(be); anyBin = true; }
            });
            slotEls.push(sw);
        });
        if (rfActive() && !partyMatch && !anyBin) return null;

        var wrap = el('div', 'tnode tnode--party');
        wrap.dataset.id = party.id;
        var row = el('div', 'tnode__row');
        row.appendChild(el('span', 'tnode__icon', '🏢'));
        row.appendChild(el('span', 'tnode__name', party.name));
        row.appendChild(nodeActions([
            { act: 'rename-party', label: '✎', title: 'Rename party' },
            { act: 'del-party', label: '🗑', title: 'Delete party' },
        ]));
        wrap.appendChild(row);
        slotEls.forEach(function (sw) { wrap.appendChild(sw); });
        return wrap;
    }
    function binNode(bin, placedMap, ancMatched) {
        var matched = ancMatched || rfHit(bin.name || '');
        var placed = placedMap[bin.id] || [];
        var shownFiles = (rfActive() && !matched) ? placed.filter(fileRowMatches) : placed;
        if (rfActive() && !matched && !shownFiles.length) return null;
        var wrap = el('div', 'tnode tnode--bin');
        wrap.dataset.id = bin.id;
        var row = el('div', 'tnode__row');
        row.appendChild(el('span', 'tnode__name', bin.name || '(invalid — set date/seq)'));
        if (placed.length) row.appendChild(el('span', 'tnode__badge', String(placed.length)));
        row.appendChild(nodeActions([{ act: 'del', label: '🗑', title: 'Delete transmittal' }]));
        wrap.appendChild(row);
        if (shownFiles.length) wrap.appendChild(fileList(shownFiles));
        return wrap;
    }

    var STATUSES = ['---', 'IFA', 'IFB', 'IFC', 'IFD', 'IFI', 'IFP', 'IFR', 'IFU', 'REC', 'RSA', 'RSB', 'RSC', 'RSD', 'RSI', 'TBD'];
    function binForm(partyId, slot) {
        var form = el('div', 'binform');
        form.dataset.party = partyId;
        form.dataset.slot = slot;
        var date = el('input', 'binform__date'); date.type = 'date';
        try { date.value = new Date().toISOString().slice(0, 10); } catch (_) { /* ok */ }
        var type = document.createElement('select'); type.className = 'binform__type';
        ['TRN', 'SUB'].forEach(function (t) { var o = el('option', null, t); o.value = t; type.appendChild(o); });
        var seq = el('input', 'binform__seq'); seq.type = 'text'; seq.placeholder = 'seq (e.g. 0007)';
        var status = document.createElement('select'); status.className = 'binform__status';
        STATUSES.forEach(function (s) { var o = el('option', null, s); o.value = s; status.appendChild(o); });
        var title = el('input', 'binform__title'); title.type = 'text'; title.placeholder = 'title (optional)';
        var add = el('button', 'btn btn-sm btn-primary', 'Add'); add.dataset.act = 'binadd';
        var cancel = el('button', 'btn btn-sm btn-secondary', 'Cancel'); cancel.dataset.act = 'bincancel';
        [date, type, seq, status, title, add, cancel].forEach(function (n) { form.appendChild(n); });
        return form;
    }

    // ── events ─────────────────────────────────────────────────────────────
    function closestNodeId(target) {
        var n = target.closest('.tnode');
        return n ? n.dataset.id : null;
    }
    function fileByKey(key) {
        var files = allFiles();
        for (var i = 0; i < files.length; i++) { if (C().srcKeyForFile(files[i]) === key) return files[i]; }
        return null;
    }
    // Click a placed-file row (anywhere but its editable name) → preview it.
    function previewFromTarget(e) {
        if (e.target.closest('.tfile__name')) return false;
        var tf = e.target.closest('.tfile');
        if (!tf || !tf.dataset.key) return false;
        var f = fileByKey(tf.dataset.key);
        if (f && window.app.modules.preview && window.app.modules.preview.previewFile) {
            window.app.modules.preview.previewFile(f);
        }
        return true;
    }
    // Edited a placed-file's ZDDC filename → re-derive its tracking placement
    // (creating the folder path if needed) + its title override.
    function onFileNameChange(e) {
        var input = e.target.closest('.tfile__name');
        if (input) commitFilenameEdit(input);
    }
    function commitFilenameEdit(input) {
        var tf = input.closest('.tfile');
        if (!tf || !tf.dataset.key) return;
        var parsed = window.zddc.parseFilename((input.value || '').trim());
        if (!parsed || !parsed.valid) {
            window.zddc.toast('Not a valid ZDDC filename — expected "TRACKING_REV (STATUS) - Title.ext".', 'warning');
            render();   // restore the derived value
            return;
        }
        var stem = parsed.trackingNumber + '_' + parsed.revision + ' (' + parsed.status + ')';
        var leaf = C().addTrackingPath(null, C().parseFolderLevels(stem));
        C().place([tf.dataset.key], leaf, 'tracking');
        if (parsed.title != null) C().setTitleOverride(tf.dataset.key, parsed.title);
        // place/setTitleOverride fire classify.notify → re-render.
    }
    // Collapse/expand a node and its whole subtree (ctrl/cmd-click a toggle).
    function setSubtreeCollapsed(nodeId, collapse) {
        var node = C().getNode(nodeId);
        if (!node) return;
        (function walk(n) {
            if ((n.children || []).length) { if (collapse) collapsed[n.id] = true; else delete collapsed[n.id]; }
            (n.children || []).forEach(walk);
        })(node);
    }
    function onTrackingClick(e) {
        if (previewFromTarget(e)) return;
        var btn = e.target.closest('[data-act]');
        if (!btn) return;
        var act = btn.dataset.act;
        var id = closestNodeId(btn);
        if (act === 'toggle') {
            var collapse = !collapsed[id];
            if (e.ctrlKey || e.metaKey) setSubtreeCollapsed(id, collapse);
            else if (collapse) collapsed[id] = true; else delete collapsed[id];
            render();
            return;
        }
        if (act === 'add') {
            var name = prompt('Child folder name (next tracking segment, or a leaf revision like "A (IFR)").\n'
                + 'Brace patterns expand: {PM,EL,EM}-MOM-{0001-0002,0005}_A (IFR)', '');
            addFoldersFromPattern(id, name);
        } else if (act === 'rename') {
            var node = C().getNode(id);
            var nn = prompt('Rename folder:', node ? node.name : '');
            if (nn && nn.trim()) C().renameNode(id, nn.trim());
        } else if (act === 'del') {
            if (confirm('Delete this folder and everything under it? Files placed here become unassigned.')) C().deleteNode(id);
        }
    }
    function onTransmittalClick(e) {
        if (previewFromTarget(e)) return;
        var btn = e.target.closest('[data-act]');
        if (!btn) return;
        var act = btn.dataset.act;

        if (act === 'addbin') {
            var slotEl = btn.closest('.tslot');
            openForm = { partyId: slotEl.dataset.party, slot: slotEl.dataset.slot };
            render();
            return;
        }
        if (act === 'bincancel') { openForm = null; render(); return; }
        if (act === 'binadd') {
            var form = btn.closest('.binform');
            var meta = {
                date: form.querySelector('.binform__date').value,
                type: form.querySelector('.binform__type').value,
                seq: form.querySelector('.binform__seq').value.trim(),
                status: form.querySelector('.binform__status').value,
                title: form.querySelector('.binform__title').value.trim(),
            };
            if (!meta.date || !meta.seq) { window.zddc.toast('Transmittal needs at least a date and a sequence number.', 'warning'); return; }
            C().addTransmittalBin(form.dataset.party, form.dataset.slot, meta);
            openForm = null;   // render() fires from classify.notify()
            return;
        }

        var id = closestNodeId(btn);
        if (act === 'rename-party') {
            var node = C().getNode(id);
            var nn = prompt('Rename party (re-derives its transmittal numbers):', node ? node.name : '');
            if (nn && nn.trim()) C().renameNode(id, nn.trim());
        } else if (act === 'del-party') {
            if (confirm('Delete this party and all its transmittals? Files placed there become unassigned.')) C().deleteNode(id);
        } else if (act === 'del') {
            if (confirm('Delete this transmittal? Files placed here become unassigned.')) C().deleteNode(id);
        }
    }

    // ── drop targets ───────────────────────────────────────────────────────
    // Resolve the drop target under an event:
    //   tracking    → any folder node (.tnode)
    //   transmittal → a transmittal bin only (.tnode--bin)
    function dropTarget(target, axis) {
        var sel = axis === 'transmittal' ? '.tnode--bin' : '.tnode';
        var node = target.closest(sel);
        if (!node || !node.dataset.id) return null;
        return { id: node.dataset.id, row: node.querySelector('.tnode__row') || node };
    }
    function clearHover(container) {
        var hot = container.querySelectorAll('.drop-hover');
        for (var i = 0; i < hot.length; i++) hot[i].classList.remove('drop-hover');
    }
    function setupDropZone(container, axis) {
        container.addEventListener('dragover', function (e) {
            if (!window.app.modules.dnd.active()) return;
            var t = dropTarget(e.target, axis);
            clearHover(container);
            if (!t) return;
            e.preventDefault();
            e.dataTransfer.dropEffect = 'copy';
            t.row.classList.add('drop-hover');
        });
        container.addEventListener('dragleave', function (e) {
            if (e.target === container) clearHover(container);
        });
        container.addEventListener('drop', function (e) {
            var t = dropTarget(e.target, axis);
            clearHover(container);
            if (!t) return;
            e.preventDefault();
            var keys = window.app.modules.dnd.getDrag();
            window.app.modules.dnd.clearDrag();
            if (!keys.length) return;
            if (axis === 'tracking') placeTrackingDrop(keys, t.id);
            else C().place(keys, t.id, axis);
        });
    }

    // Tracking drop: if the target is already a complete leaf, assign directly;
    // otherwise prompt for the remaining levels (parsed + nested under it) so a
    // file can be dropped on an existing partial tracking number and completed.
    function placeTrackingDrop(keys, nodeId) {
        if (C().trackingNodeComplete(nodeId)) { C().place(keys, nodeId, 'tracking'); return; }
        var label = C().trackingPathLabel(nodeId);
        var input = prompt('Dropping under "' + label + '".\n'
            + 'Add the remaining tracking levels (e.g. "0001_0 (IFU)"), or leave blank to drop here:', '');
        if (input === null) return;                 // cancelled
        var levels = C().parseFolderLevels(input.trim());
        var target = levels.length ? C().addTrackingPath(nodeId, levels) : nodeId;
        C().place(keys, target, 'tracking');
    }

    // Reveal a source key's placement in the target pane (source → target).
    function reveal(key) {
        var a = C().getAssignment(key);
        if (!a) return;
        if (a.trackingNodeId) {
            showTab('tracking'); collapsed = {}; render();
            flashNode(els.trackingTree, a.trackingNodeId);
        } else if (a.transmittalNodeId) {
            showTab('transmittal'); render();
            flashNode(els.transmittalTree, a.transmittalNodeId);
        }
    }
    function flashNode(container, id) {
        var node = container.querySelector('.tnode[data-id="' + id + '"]');
        if (!node) return;
        node.scrollIntoView({ block: 'center' });
        var row = node.querySelector('.tnode__row') || node;
        row.classList.add('reveal-flash');
        setTimeout(function () { row.classList.remove('reveal-flash'); }, 1500);
    }

    window.app.modules.targetTree = {
        init: init,
        render: render,
        showTab: showTab,
        activeAxis: activeAxis,
        setNameFilter: setNameFilter,
        reveal: reveal,
    };
})();
