// ==========================================
// signature.js: CURSIVE SHRINK-WRAP + FORM ATTESTATION
// ==========================================
// Two consumers:
//  1) Insert Signature (N) → pending stamp on next page click
//  2) Form signature field fill → name + attestation + client meta + timestamp
//     (stored on the field; included in project schema → seal schemaHash)

const sigInput = document.getElementById("sigInput");
const sigPreviews = document.getElementById("sigPreviews");
const sigAttestBlock = document.getElementById("sigAttestationBlock");
const sigAttestCheck = document.getElementById("sigAttestCheck");
const sigAttestText = document.getElementById("sigAttestText");
const sigFonts = ["'Great Vibes', cursive", "'Dancing Script', cursive", "'Homemade Apple', cursive", "'Caveat', cursive", "'Pacifico', cursive", "Brush Script MT", "Lucida Handwriting"];

/** Fixed wording so sealed audits compare apples-to-apples. */
window.CCS_SIGNATURE_ATTESTATION_TEXT =
    "I attest that I am the person identified by the name I enter, that I intend this to be my electronic signature for this form field, and that I understand this record (signed name, time, and basic browser/device details) may be retained for audit.";

if (sigAttestText) {
    sigAttestText.textContent = window.CCS_SIGNATURE_ATTESTATION_TEXT;
}

document.getElementById("closeSigBtn").addEventListener("click", () => {
    APP.DOM.sigModal.style.display = "none";
    APP.pendingFormFieldSign = null;
    window.setSigAttestationUiVisible(false);
});

/**
 * Browser/device metadata for signature audit (no canvas fingerprinting).
 */
window.collectSigningClientMeta = function() {
    const nav = typeof navigator !== "undefined" ? navigator : {};
    const scr = typeof screen !== "undefined" ? screen : {};
    let timezone = null;
    try {
        timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || null;
    } catch (_) { /* ignore */ }
    return {
        userAgent: nav.userAgent || null,
        language: nav.language || null,
        languages: Array.isArray(nav.languages) ? nav.languages.slice(0, 8) : null,
        platform: nav.platform || null,
        vendor: nav.vendor || null,
        cookieEnabled: typeof nav.cookieEnabled === "boolean" ? nav.cookieEnabled : null,
        hardwareConcurrency: nav.hardwareConcurrency != null ? nav.hardwareConcurrency : null,
        deviceMemory: nav.deviceMemory != null ? nav.deviceMemory : null,
        maxTouchPoints: nav.maxTouchPoints != null ? nav.maxTouchPoints : null,
        screen: {
            width: scr.width || null,
            height: scr.height || null,
            availWidth: scr.availWidth || null,
            availHeight: scr.availHeight || null,
            colorDepth: scr.colorDepth || null,
            pixelDepth: scr.pixelDepth || null,
            devicePixelRatio: typeof window !== "undefined" ? window.devicePixelRatio : null
        },
        timezone,
        timezoneOffsetMin: new Date().getTimezoneOffset(),
        href: (typeof location !== "undefined" && location.href) ? String(location.href).slice(0, 500) : null,
        referrer: (typeof document !== "undefined" && document.referrer)
            ? String(document.referrer).slice(0, 300)
            : null
    };
};

window.setSigAttestationUiVisible = function(show) {
    if (sigAttestBlock) {
        sigAttestBlock.style.display = show ? "block" : "none";
        if (show) sigAttestBlock.removeAttribute("hidden");
        else sigAttestBlock.setAttribute("hidden", "");
    }
    if (sigAttestCheck) {
        sigAttestCheck.checked = false;
        sigAttestCheck.required = !!show;
    }
};

/**
 * Open the signature modal to fill a designer form "signature" field.
 */
window.openSignatureForFormField = function(el, btn) {
    if (!el) return;
    APP.pendingFormFieldSign = { el, btn };
    APP.pendingSignatureSrc = null;
    window.setSigAttestationUiVisible(true);
    if (APP.DOM.sigModal) {
        APP.DOM.sigModal.style.display = "flex";
        if (sigInput) {
            const prev = el.dataset.defaultValue || "";
            sigInput.value = (prev && prev.indexOf("data:image") !== 0) ? prev : "";
            sigInput.focus();
            sigInput.dispatchEvent(new Event("input", { bubbles: true }));
        }
    }
};

/**
 * Build attestation record for a form signature (hashed into project seal via schema).
 */
window.buildSignatureAttestation = async function(el, signedName, extra) {
    const name = String(signedName || "").trim();
    const signedAt = new Date().toISOString();
    const client = window.collectSigningClientMeta();
    const record = {
        v: 1,
        signedName: name,
        signedAt,
        attestationText: window.CCS_SIGNATURE_ATTESTATION_TEXT,
        attested: true,
        fieldName: (el && el.dataset && el.dataset.fieldName) || null,
        fieldId: (el && el.id) || null,
        client,
        originHash: (window.APP && APP.documentOrigin && APP.documentOrigin.hash) || null
    };
    if (extra && typeof extra === "object") {
        if (extra.font) record.font = String(extra.font).slice(0, 80);
        if (extra.method) record.method = String(extra.method).slice(0, 40);
    }
    // Digest for quick seal/audit references (full record also stored)
    try {
        const stable = (window.CcsProjectState && window.CcsProjectState.stableStringify)
            ? window.CcsProjectState.stableStringify(record)
            : JSON.stringify(record);
        if (window.CcsProjectState && typeof window.CcsProjectState.sha256Hex === "function") {
            record.attestationHash = await window.CcsProjectState.sha256Hex(
                new TextEncoder().encode(stable)
            );
        }
    } catch (e) {
        console.warn("[signature] attestationHash failed", e);
    }
    return record;
};

/**
 * Format ISO timestamp for compact caption display.
 */
window.formatSignatureCaptionTime = function(iso) {
    if (!iso) return "";
    try {
        const d = new Date(iso);
        if (Number.isNaN(d.getTime())) return String(iso);
        // Compact: 2026-08-05 14:32
        const pad = (n) => String(n).padStart(2, "0");
        return (
            d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) +
            " " + pad(d.getHours()) + ":" + pad(d.getMinutes())
        );
    } catch (_) {
        return String(iso);
    }
};

/** @type {HTMLElement|null} */
let _sigAttPopoverEl = null;
/** @type {ReturnType<typeof setTimeout>|null} */
let _sigAttPopoverHideTimer = null;
/** @type {Element|null} */
let _sigAttPopoverAnchor = null;

window.copyTextToClipboard = async function(text) {
    const s = String(text || "");
    if (!s) return false;
    try {
        if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
            await navigator.clipboard.writeText(s);
            return true;
        }
    } catch (_) { /* fall through */ }
    try {
        const ta = document.createElement("textarea");
        ta.value = s;
        ta.setAttribute("readonly", "");
        ta.style.cssText = "position:fixed;left:-9999px;top:0;opacity:0;";
        document.body.appendChild(ta);
        ta.select();
        const ok = document.execCommand("copy");
        ta.remove();
        return !!ok;
    } catch (_) {
        return false;
    }
};

window.hideSigAttestationPopover = function(immediate) {
    if (_sigAttPopoverHideTimer) {
        clearTimeout(_sigAttPopoverHideTimer);
        _sigAttPopoverHideTimer = null;
    }
    const hide = () => {
        if (_sigAttPopoverEl) {
            // Never leave focus inside an aria-hidden ancestor (a11y console warning)
            const ae = document.activeElement;
            if (ae && _sigAttPopoverEl.contains(ae)) {
                try { ae.blur(); } catch (_) { /* ignore */ }
            }
            _sigAttPopoverEl.classList.remove("is-visible");
            _sigAttPopoverEl.setAttribute("aria-hidden", "true");
            try { _sigAttPopoverEl.inert = true; } catch (_) { /* older engines */ }
        }
        _sigAttPopoverAnchor = null;
    };
    if (immediate) hide();
    else {
        _sigAttPopoverHideTimer = setTimeout(hide, 160);
    }
};

/**
 * Body-level popover (escapes field overflow:hidden). Full hashes + copy.
 * @param {Element} anchorEl
 * @param {{ name: string, when: string, attested: boolean, originFull: string, attFull: string }} data
 */
window.showSigAttestationPopover = function(anchorEl, data) {
    if (!anchorEl || !data) return;
    if (_sigAttPopoverHideTimer) {
        clearTimeout(_sigAttPopoverHideTimer);
        _sigAttPopoverHideTimer = null;
    }

    if (!_sigAttPopoverEl) {
        const pop = document.createElement("div");
        pop.id = "sigAttestationPopover";
        pop.className = "sig-att-popover";
        // Interactive panel (Copy) — not a pure tooltip; role=tooltip + aria-hidden fights focus
        pop.setAttribute("role", "region");
        pop.setAttribute("aria-label", "Signature attestation hashes");
        pop.setAttribute("aria-hidden", "true");
        try { pop.inert = true; } catch (_) { /* ignore */ }
        pop.addEventListener("mouseenter", () => {
            if (_sigAttPopoverHideTimer) {
                clearTimeout(_sigAttPopoverHideTimer);
                _sigAttPopoverHideTimer = null;
            }
        });
        pop.addEventListener("mouseleave", () => {
            window.hideSigAttestationPopover(false);
        });
        // Keep popover open while interacting; don't bubble to page drag
        pop.addEventListener("mousedown", (ev) => ev.stopPropagation());
        pop.addEventListener("click", (ev) => ev.stopPropagation());
        document.body.appendChild(pop);
        _sigAttPopoverEl = pop;
    }

    const pop = _sigAttPopoverEl;
    _sigAttPopoverAnchor = anchorEl;

    const esc = (s) => String(s || "")
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;");

    const name = data.name || "";
    const when = data.when || "";
    const originFull = data.originFull || "";
    const attFull = data.attFull || "";

    let head = `<div class="sig-att-pop-head">`;
    head += `<span class="sig-att-pop-name">${esc(name)}</span>`;
    if (data.attested) head += ` <span class="sig-att-pop-ok">✓ attested</span>`;
    if (when) head += `<div class="sig-att-pop-when">${esc(when)}</div>`;
    head += `</div>`;

    const row = (label, full) => {
        if (!full) return "";
        return (
            `<div class="sig-att-pop-row">` +
            `<div class="sig-att-pop-label">${esc(label)}</div>` +
            `<div class="sig-att-pop-hash-row">` +
            `<code class="sig-att-pop-hash" title="${esc(full)}">${esc(full)}</code>` +
            `<button type="button" class="sig-att-pop-copy" data-copy="${esc(full)}">Copy</button>` +
            `</div></div>`
        );
    };

    let body = row("Document originHash", originFull) + row("Signature attestationHash", attFull);
    if (!body) {
        body = `<div class="sig-att-pop-empty">No hashes stored on this signature yet.</div>`;
    }

    pop.innerHTML =
        head +
        `<div class="sig-att-pop-body">${body}</div>` +
        `<div class="sig-att-pop-foot">Hover to inspect · click caption strip to re-sign</div>`;

    pop.querySelectorAll(".sig-att-pop-copy").forEach((btn) => {
        btn.addEventListener("click", async (ev) => {
            ev.preventDefault();
            ev.stopPropagation();
            const val = btn.getAttribute("data-copy") || "";
            const ok = await window.copyTextToClipboard(val);
            const prev = btn.textContent;
            btn.textContent = ok ? "Copied" : "Failed";
            btn.classList.toggle("is-ok", !!ok);
            btn.classList.toggle("is-err", !ok);
            // Drop focus after activate so a later hide does not aria-hide a focused control
            try { btn.blur(); } catch (_) { /* ignore */ }
            setTimeout(() => {
                btn.textContent = prev || "Copy";
                btn.classList.remove("is-ok", "is-err");
            }, 1200);
        });
    });

    // Position: prefer above anchor; flip below if clipped
    try { pop.inert = false; } catch (_) { /* ignore */ }
    pop.classList.add("is-visible");
    pop.setAttribute("aria-hidden", "false");
    pop.style.left = "0px";
    pop.style.top = "0px";
    // Force layout for size
    const pr = pop.getBoundingClientRect();
    const ar = anchorEl.getBoundingClientRect();
    const pad = 8;
    const gap = 6;
    let left = ar.left + (ar.width / 2) - (pr.width / 2);
    left = Math.max(pad, Math.min(left, window.innerWidth - pr.width - pad));
    let top = ar.top - pr.height - gap;
    let place = "above";
    if (top < pad) {
        top = ar.bottom + gap;
        place = "below";
        if (top + pr.height > window.innerHeight - pad) {
            top = Math.max(pad, window.innerHeight - pr.height - pad);
        }
    }
    pop.style.left = Math.round(left) + "px";
    pop.style.top = Math.round(top) + "px";
    pop.dataset.place = place;
};

/**
 * Small caption under a signed form field: name, attestation confirm, time, truncated hashes.
 * Full hashes + copy live in a body-level hover popover (native title is unreliable).
 * @param {Element} el formFieldOverlay
 * @param {Element} [fillHost] .form-field-fill (defaults to el's fill)
 * @returns {HTMLElement|null}
 */
window.renderFormSignatureAttestationCaption = function(el, fillHost) {
    if (!el) return null;
    const fill = fillHost || el.querySelector(".form-field-fill");
    if (!fill) return null;

    let att = null;
    try {
        if (el.dataset.sigAttestation) att = JSON.parse(el.dataset.sigAttestation);
    } catch (_) { att = null; }

    let cap = fill.querySelector(".form-field-sig-attestation");
    if (!att || !att.signedName) {
        if (cap) {
            if (_sigAttPopoverAnchor === cap) window.hideSigAttestationPopover(true);
            cap.remove();
        }
        return null;
    }

    if (!cap) {
        cap = document.createElement("div");
        cap.className = "form-field-sig-attestation";
        cap.setAttribute("aria-label", "Signature attestation");
        fill.appendChild(cap);
    }

    const name = String(att.signedName || "").trim();
    const when = window.formatSignatureCaptionTime(att.signedAt);
    const originFull = att.originHash ? String(att.originHash) : "";
    const attFull = att.attestationHash
        ? String(att.attestationHash)
        : (el.dataset.sigAttestationHash ? String(el.dataset.sigAttestationHash) : "");
    // Mid-length on separate lines; full values only in the hover popover
    const originShort = originFull
        ? originFull.slice(0, 16) + "…" + originFull.slice(-8)
        : "";
    const attShort = attFull
        ? attFull.slice(0, 16) + "…" + attFull.slice(-8)
        : "";

    const esc = (s) => String(s || "")
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;");

    let primary = `<span class="sig-att-name">${esc(name)}</span>`;
    primary += ` <span class="sig-att-confirm">✓</span>`;
    if (when) primary += ` <span class="sig-att-when">${esc(when)}</span>`;

    let meta = "";
    if (originShort) {
        meta += `<span class="sig-att-meta">doc ${esc(originShort)}</span>`;
    }
    if (attShort) {
        meta += `<span class="sig-att-meta">sig ${esc(attShort)}</span>`;
    }

    cap.innerHTML = `<span class="sig-att-primary">${primary}</span>${meta}`;

    const popData = {
        name,
        when: when || (att.signedAt ? String(att.signedAt) : ""),
        attested: !!att.attested,
        originFull,
        attFull
    };
    // Stash for re-show after re-render without reparsing
    cap._sigAttPopData = popData;

    // Short native tip only (real detail is the popover)
    const shortTip = "Hover for full hashes · click to re-sign";
    cap.removeAttribute("title");
    cap.setAttribute("data-tip", shortTip);

    const btn = fill.querySelector(".form-field-fill-sig");
    if (btn) {
        btn.title = shortTip;
        // Ink hover also opens the popover so either hit target works
        if (!btn.dataset.boundSigPop) {
            btn.dataset.boundSigPop = "1";
            btn.addEventListener("mouseenter", () => {
                const c = fill.querySelector(".form-field-sig-attestation");
                if (c && c._sigAttPopData) {
                    window.showSigAttestationPopover(c, c._sigAttPopData);
                }
            });
            btn.addEventListener("mouseleave", () => {
                window.hideSigAttestationPopover(false);
            });
        }
    }

    // Hover caption → popover; leave with grace so pointer can enter popover
    if (!cap.dataset.boundPop) {
        cap.dataset.boundPop = "1";
        cap.addEventListener("mouseenter", () => {
            if (cap._sigAttPopData) {
                window.showSigAttestationPopover(cap, cap._sigAttPopData);
            }
        });
        cap.addEventListener("mouseleave", () => {
            window.hideSigAttestationPopover(false);
        });
    }

    // Click caption → re-open sign (does not steal the whole field)
    if (!cap.dataset.boundClick) {
        cap.dataset.boundClick = "1";
        cap.addEventListener("click", (ev) => {
            ev.preventDefault();
            ev.stopPropagation();
            window.hideSigAttestationPopover(true);
            const b = fill.querySelector(".form-field-fill-sig");
            if (typeof window.openSignatureForFormField === "function") {
                window.openSignatureForFormField(el, b || null);
            } else if (b) {
                b.click();
            }
        });
    }

    // Designer chrome hint (Form mode)
    const hint = el.querySelector(".form-field-hint");
    if (hint) {
        hint.textContent = name + (when ? " · " + when : "");
        hint.title = shortTip;
    }

    return cap;
};

// Hide popover on scroll/resize so it doesn't float away from the field
(function bindSigAttPopoverDismiss() {
    const dismiss = () => {
        if (_sigAttPopoverEl && _sigAttPopoverEl.classList.contains("is-visible")) {
            window.hideSigAttestationPopover(true);
        }
    };
    window.addEventListener("scroll", dismiss, true);
    window.addEventListener("resize", dismiss);
    document.addEventListener("keydown", (e) => {
        if (e.key === "Escape") dismiss();
    });
})();

/**
 * Apply signature + attestation to a form field shell.
 */
window.applySignatureToFormField = async function(el, btn, name, dataUrl, extra) {
    if (!el) return;
    const text = String(name || "").trim();
    el.dataset.defaultValue = text;
    if (dataUrl) el.dataset.sigImage = dataUrl;
    else delete el.dataset.sigImage;

    const attestation = await window.buildSignatureAttestation(el, text, extra);
    try {
        el.dataset.sigAttestation = JSON.stringify(attestation);
    } catch (e) {
        console.warn("[signature] could not store attestation JSON", e);
    }
    if (attestation.attestationHash) {
        el.dataset.sigAttestationHash = attestation.attestationHash;
    }

    if (btn) {
        if (dataUrl) {
            btn.style.backgroundImage = "url(" + dataUrl + ")";
            btn.style.backgroundSize = "contain";
            btn.style.backgroundRepeat = "no-repeat";
            btn.style.backgroundPosition = "center";
            btn.style.color = "transparent";
            btn.textContent = text || "Signed ✓";
        } else {
            btn.style.backgroundImage = "";
            btn.style.color = "";
            btn.textContent = text || "Click to sign…";
        }
    }

    // Caption + shared hover tip (name, time, full hashes)
    if (typeof window.renderFormSignatureAttestationCaption === "function") {
        window.renderFormSignatureAttestationCaption(el);
    }

    if (typeof window.logProjectAudit === "function") {
        window.logProjectAudit("FIELD_SIGNED", {
            fieldName: el.dataset.fieldName,
            fieldType: "signature",
            signedName: text,
            signedAt: attestation.signedAt,
            attestationHash: attestation.attestationHash || null,
            platform: attestation.client && attestation.client.platform,
            language: attestation.client && attestation.client.language,
            timezone: attestation.client && attestation.client.timezone,
            userAgent: attestation.client && attestation.client.userAgent
                ? String(attestation.client.userAgent).slice(0, 160)
                : null,
            method: (extra && extra.method) || "modal"
        });
    }

    console.log(
        "%c[CCS form signature]",
        "font-weight:bold;color:#7c3aed",
        "\n  field     =", el.dataset.fieldName,
        "\n  name      =", text,
        "\n  signedAt  =", attestation.signedAt,
        "\n  attHash   =", attestation.attestationHash && attestation.attestationHash.slice(0, 16) + "…",
        "\n  platform  =", attestation.client && attestation.client.platform,
        "\n  timezone  =", attestation.client && attestation.client.timezone,
        "\n  userAgent =", attestation.client && attestation.client.userAgent
    );
};

/**
 * Open signature modal for page stamps (no attestation checkbox).
 */
window.openSignatureModalForStamp = function() {
    APP.pendingFormFieldSign = null;
    APP.pendingSignatureSrc = null;
    window.setSigAttestationUiVisible(false);
    if (APP.DOM.sigModal) {
        APP.DOM.sigModal.style.display = "flex";
        if (sigInput) {
            sigInput.focus();
            if (sigInput.value) sigInput.dispatchEvent(new Event("input", { bubbles: true }));
        }
    }
};

sigInput.addEventListener("input", () => {
    sigPreviews.innerHTML = "";
    const text = sigInput.value.trim();
    if (!text) return;
    document.fonts.ready.then(() => {
        sigFonts.forEach((font) => {
            const canvas = document.createElement("canvas");
            const ctx = canvas.getContext("2d");

            ctx.font = `40px ${font}`;
            const metrics = ctx.measureText(text);
            const inkWidth = metrics.actualBoundingBoxLeft + metrics.actualBoundingBoxRight;
            const inkAscent = metrics.actualBoundingBoxAscent;
            const inkDescent = metrics.actualBoundingBoxDescent;
            const inkHeight = inkAscent + inkDescent;

            canvas.width = Math.max(10, inkWidth + 8);
            canvas.height = Math.max(10, inkHeight + 8);

            ctx.font = `40px ${font}`;
            ctx.fillStyle = "#000000";
            ctx.fillText(text, 4 + metrics.actualBoundingBoxLeft, 4 + inkAscent);

            const img = document.createElement("img");
            img.src = canvas.toDataURL("image/png");
            img.className = "sig-preview-img";
            img.title = `Click to use ${font.replace(/'/g, "")}`;

            const descentRatio = inkDescent / canvas.height;

            img.addEventListener("click", () => {
                // Form-field fill path — require attestation
                if (APP.pendingFormFieldSign && APP.pendingFormFieldSign.el) {
                    if (sigAttestCheck && !sigAttestCheck.checked) {
                        if (typeof window.customAlert === "function") {
                            window.customAlert(
                                "Please check the attestation box to confirm you are the person signing before choosing a signature style.",
                                "Attestation required"
                            );
                        } else {
                            window.alert("Please check the attestation box before signing.");
                        }
                        return;
                    }
                    const { el, btn } = APP.pendingFormFieldSign;
                    Promise.resolve(
                        window.applySignatureToFormField(el, btn, text, img.src, {
                            font,
                            method: "modal-cursive"
                        })
                    ).then(() => {
                        APP.pendingFormFieldSign = null;
                        window.setSigAttestationUiVisible(false);
                        APP.DOM.sigModal.style.display = "none";
                    });
                    return;
                }
                // Page stamp path (Insert Signature / N)
                APP.pendingSignatureSrc = img.src;
                APP.pendingSignatureDescent = descentRatio;
                APP.DOM.sigModal.style.display = "none";
                if (APP.DOM.viewer) APP.DOM.viewer.style.cursor = "crosshair";
                window.clearActiveOverlay();
            });
            sigPreviews.appendChild(img);
        });
    });
});
