// accept-transmittal.js — the doc-controller "Accept Transmittal"
// workflow modal.
//
// Surfaced by events.js as a right-click item on a transmittal folder
// inside archive/<their-party>/incoming/. The folder name must conform
// to the ZDDC transmittal grammar (date_tracking (status) - title);
// every file inside must conform to ZDDC filename grammar with the
// same tracking. Non-conformance is flagged in the modal and the user
// cancels to ask the sender to fix.
//
// On submit, the form assembles a YAML body (received_date plus an
// optional plan-review chain block) and POSTs it with
// X-ZDDC-Op: accept-transmittal to the transmittal-folder URL. The
// server validates everything, moves the folder into received/,
// renames it to tracking-only, and optionally chains Plan Review.

(function () {
    'use strict';

    var REVIEW_OFFSET_DAYS = 7;
    var RESPONSE_OFFSET_DAYS = 14;

    function status(msg, level) {
        var t = window.zddc && window.zddc.toast;
        if (t) t(msg, level || 'info');
    }

    function isoDateToday() {
        var d = new Date();
        return d.getFullYear()
            + '-' + ('0' + (d.getMonth() + 1)).slice(-2)
            + '-' + ('0' + d.getDate()).slice(-2);
    }
    function isoDatePlus(days) {
        var d = new Date();
        d.setDate(d.getDate() + days);
        return d.getFullYear()
            + '-' + ('0' + (d.getMonth() + 1)).slice(-2)
            + '-' + ('0' + d.getDate()).slice(-2);
    }

    function escapeHtml(s) {
        return String(s).replace(/[&<>"']/g, function (c) {
            return ({
                '&': '&amp;', '<': '&lt;', '>': '&gt;',
                '"': '&quot;', "'": '&#39;'
            })[c];
        });
    }

    // Is this node a direct child of an incoming/ canonical folder
    // AND a well-formed transmittal folder? The first half is the
    // cascade-driven scope check (X-ZDDC-Canonical-Folder == 'incoming'
    // on the current listing's parent context); the second is a
    // structural folder-name parse against the ZDDC grammar.
    function isAcceptableTransmittalFolder(node) {
        if (!node || !node.isDir) return false;
        if (node.virtual) return false;
        // The cascade signal is on the PARENT directory's listing, which
        // is the directory whose contents are currently shown — i.e.
        // state.currentPath. When the listing's scope is incoming/,
        // every direct child folder is a candidate (validated by name
        // here and by the server again on POST).
        if (window.app.state.scopeCanonicalFolder !== 'incoming') return false;
        var parsed = window.zddc.parseFolder(node.name);
        return !!(parsed && parsed.valid);
    }

    // Scan the listing's tree node for files inside the transmittal
    // folder and classify each as conforming (tracking matches the
    // folder) or violating. Returns { ok: [...], violations: [...] }.
    // Best-effort — operates only on already-loaded children. The
    // server is authoritative; this is a UX hint.
    function classifyChildren(node, folderTracking) {
        var out = { ok: [], violations: [] };
        var children = (node && node.children) ? node.children : [];
        children.forEach(function (c) {
            if (c.virtual) return;
            if (c.isDir) {
                out.violations.push(c.name + ': nested directories are not permitted');
                return;
            }
            if (c.name.charAt(0) === '.') return; // dotfiles ignored
            var parsed = window.zddc.parseFilename(c.name);
            if (!parsed || !parsed.valid) {
                out.violations.push(c.name + ': does not conform to ZDDC filename grammar');
                return;
            }
            if (parsed.trackingNumber !== folderTracking) {
                out.violations.push(c.name + ': tracking "' + parsed.trackingNumber
                    + '" does not match folder tracking "' + folderTracking + '"');
                return;
            }
            out.ok.push(c.name);
        });
        return out;
    }

    function fetchPeopleSuggestions() {
        return fetch('/.profile/access', {
            headers: { 'Accept': 'application/json' },
            credentials: 'same-origin'
        }).then(function (r) {
            if (!r.ok) return [];
            return r.json().then(function (data) {
                var out = [];
                if (data && data.email) out.push(data.email);
                return out;
            });
        }).catch(function () { return []; });
    }

    function openForm(initial) {
        return new Promise(function (resolve, reject) {
            var overlay = document.createElement('div');
            overlay.className = 'modal-overlay';
            overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.4);display:flex;align-items:center;justify-content:center;z-index:9000;';

            var box = document.createElement('div');
            box.style.cssText = 'background:var(--bg,#fff);color:var(--fg,#111);padding:1.25rem 1.5rem;border-radius:6px;min-width:28rem;max-width:36rem;box-shadow:0 4px 20px rgba(0,0,0,0.25);font-family:inherit;';

            var violationsHtml = '';
            if (initial.violations && initial.violations.length) {
                violationsHtml = '<div style="margin:0.5rem 0;padding:0.5rem 0.75rem;background:#fff3cd;border-left:3px solid #d39e00;font-size:0.85rem;">'
                    + '<strong>Non-conforming files detected:</strong><ul style="margin:0.25rem 0 0 1rem;padding:0;">'
                    + initial.violations.map(function (v) { return '<li>' + escapeHtml(v) + '</li>'; }).join('')
                    + '</ul><p style="margin:0.4rem 0 0 0;">Cancel and contact the sender to correct these before re-uploading.</p></div>';
            }

            var planReviewFieldsHtml =
                '<div id="acc-pr-fields" style="display:none;margin-top:0.6rem;padding:0.5rem 0.75rem;background:rgba(0,0,0,0.03);border-radius:4px;">' +
                  '<div style="display:grid;grid-template-columns:max-content 1fr;gap:0.4rem 0.75rem;align-items:center;font-size:0.9rem;">' +
                    '<label for="acc-review-lead">Review lead</label>' +
                    '<input id="acc-review-lead" type="email" list="acc-people" style="padding:0.25rem 0.4rem;" placeholder="email — becomes sub-admin of reviewing/<…>">' +
                    '<label for="acc-review-date">Plan review complete date</label>' +
                    '<input id="acc-review-date" type="date">' +
                    '<label for="acc-approver">Approver</label>' +
                    '<input id="acc-approver" type="email" list="acc-people" style="padding:0.25rem 0.4rem;" placeholder="email — becomes sub-admin of staging/<…>">' +
                    '<label for="acc-response-date">Plan response date</label>' +
                    '<input id="acc-response-date" type="date">' +
                    '<datalist id="acc-people"></datalist>' +
                  '</div>' +
                '</div>';

            box.innerHTML =
                '<h2 style="margin:0 0 0.5rem 0;font-size:1.1rem;">Accept Transmittal — ' + escapeHtml(initial.tracking) + '</h2>' +
                '<p style="margin:0 0 0.5rem 0;font-size:0.85rem;color:#666;">' +
                  'This will file <strong>' + initial.fileCount + ' file' + (initial.fileCount === 1 ? '' : 's') + '</strong> from ' +
                  '<code>' + escapeHtml(initial.folder) + '</code> into the immutable received archive at ' +
                  '<code>archive/' + escapeHtml(initial.party) + '/received/' + escapeHtml(initial.tracking) + '/</code>. ' +
                  'Once filed, only document-control can add new files there; nothing can be edited or deleted.' +
                '</p>' +
                violationsHtml +
                '<div style="display:grid;grid-template-columns:max-content 1fr;gap:0.5rem 0.75rem;align-items:center;font-size:0.9rem;">' +
                  '<label for="acc-received-date">Received date</label>' +
                  '<input id="acc-received-date" type="date" required>' +
                '</div>' +
                '<label style="display:flex;align-items:center;gap:0.4rem;margin-top:0.8rem;font-size:0.9rem;">' +
                  '<input type="checkbox" id="acc-setup-pr">' +
                  '<span>Set up Plan Review now — scaffold the reviewing/ and staging/ folders for the response</span>' +
                '</label>' +
                planReviewFieldsHtml +
                '<div style="display:flex;justify-content:flex-end;gap:0.5rem;margin-top:1rem;">' +
                  '<button type="button" id="acc-cancel">Cancel</button>' +
                  '<button type="button" id="acc-submit" class="btn-primary"' +
                    (initial.violations && initial.violations.length ? ' disabled' : '') + '>Accept</button>' +
                '</div>';

            overlay.appendChild(box);
            document.body.appendChild(overlay);

            box.querySelector('#acc-received-date').value = isoDateToday();
            box.querySelector('#acc-review-date').value = isoDatePlus(REVIEW_OFFSET_DAYS);
            box.querySelector('#acc-response-date').value = isoDatePlus(RESPONSE_OFFSET_DAYS);

            var prCheckbox = box.querySelector('#acc-setup-pr');
            var prFields = box.querySelector('#acc-pr-fields');
            prCheckbox.addEventListener('change', function () {
                prFields.style.display = prCheckbox.checked ? '' : 'none';
            });

            fetchPeopleSuggestions().then(function (emails) {
                var dl = box.querySelector('#acc-people');
                if (!dl) return;
                emails.forEach(function (e) {
                    var opt = document.createElement('option');
                    opt.value = e;
                    dl.appendChild(opt);
                });
            });

            function close() {
                if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
            }
            box.querySelector('#acc-cancel').addEventListener('click', function () {
                close(); reject(new Error('cancelled'));
            });
            overlay.addEventListener('click', function (e) {
                if (e.target === overlay) { close(); reject(new Error('cancelled')); }
            });
            document.addEventListener('keydown', function escHandler(e) {
                if (e.key === 'Escape') {
                    document.removeEventListener('keydown', escHandler);
                    close(); reject(new Error('cancelled'));
                }
            });

            box.querySelector('#acc-submit').addEventListener('click', function () {
                var values = {
                    receivedDate: box.querySelector('#acc-received-date').value,
                    setupPlanReview: prCheckbox.checked,
                    reviewLead: box.querySelector('#acc-review-lead').value.trim(),
                    approver: box.querySelector('#acc-approver').value.trim(),
                    planReviewDate: box.querySelector('#acc-review-date').value,
                    planResponseDate: box.querySelector('#acc-response-date').value
                };
                if (!values.receivedDate) { status('Received date is required.', 'error'); return; }
                if (values.setupPlanReview) {
                    if (!values.reviewLead || !values.approver
                        || !values.planReviewDate || !values.planResponseDate) {
                        status('Plan Review fields are required when the checkbox is on.', 'error');
                        return;
                    }
                }
                close(); resolve(values);
            });
        });
    }

    function quote(s) {
        return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
    }
    function buildBody(values) {
        var lines = ['received_date: ' + values.receivedDate];
        if (values.setupPlanReview) {
            lines.push('setup_plan_review: true');
            lines.push('review_lead: ' + quote(values.reviewLead));
            lines.push('approver: ' + quote(values.approver));
            lines.push('plan_review_complete_date: ' + values.planReviewDate);
            lines.push('plan_response_date: ' + values.planResponseDate);
        }
        lines.push('');
        return lines.join('\n');
    }

    async function invoke(node) {
        var tree = window.app.modules.tree;
        if (!tree) return;
        var url = tree.pathFor(node);
        if (!url.endsWith('/')) url += '/';

        var parsedFolder = window.zddc.parseFolder(node.name);
        if (!parsedFolder || !parsedFolder.valid) {
            status('Folder name does not conform to ZDDC transmittal grammar.', 'error');
            return;
        }

        // Derive the party from the path: archive/<party>/incoming/<folder>/.
        var parts = url.replace(/^\/+|\/+$/g, '').split('/');
        var partyIdx = parts.indexOf('archive');
        var party = (partyIdx >= 0 && parts[partyIdx + 1]) ? parts[partyIdx + 1] : '';

        var classification = classifyChildren(node, parsedFolder.trackingNumber);

        var values;
        try {
            values = await openForm({
                tracking: parsedFolder.trackingNumber,
                folder: node.name,
                party: party,
                fileCount: classification.ok.length,
                violations: classification.violations
            });
        } catch (_e) {
            return;
        }

        status('Accept Transmittal — submitting…');
        var resp;
        try {
            resp = await fetch(url, {
                method: 'POST',
                headers: {
                    'X-ZDDC-Op': 'accept-transmittal',
                    'Content-Type': 'application/yaml'
                },
                body: buildBody(values),
                credentials: 'same-origin'
            });
        } catch (e) {
            status('Accept failed: ' + (e && e.message ? e.message : e), 'error');
            return;
        }
        if (!resp.ok) {
            var text = '';
            try { text = await resp.text(); } catch (_e) { /* ignore */ }
            status('Accept failed (' + resp.status + '): ' + text, 'error');
            return;
        }
        var data; try { data = await resp.json(); } catch (_e) { data = null; }
        var msg = 'Accepted ' + (data && data.moved_files ? data.moved_files : '?') + ' file(s) into '
            + (data && data.received_path ? data.received_path : 'received/');
        if (data && data.merged) msg += ' (merged with existing tracking)';
        if (data && data.plan_review) msg += ' · Plan Review scaffolded';
        status(msg + ' — reload to see the move.', 'success');
    }

    window.app.modules.acceptTransmittal = {
        isAcceptableTransmittalFolder: isAcceptableTransmittalFolder,
        invoke: invoke
    };
})();
