// ==========================================
// project-crypto.js: device key + seal signatures
// ==========================================
// ECDSA P-256 key pair in IndexedDB (non-extractable private key).
// Signs the seal commitment (contentHash + schemaHash + originHash + …)
// so another CCS install can verify with the embedded public JWK.
// Not a TSA / not a legal e-sign product — device-local attestation.

(function (global) {
    "use strict";

    const DB_NAME = "ccs-pdf-editor-crypto";
    const DB_VER = 1;
    const STORE = "keys";
    const KEY_ID = "device-ecdsa-p256";

    let kv = null;
    function store() {
        if (!kv && global.CcsIdb && typeof global.CcsIdb.createKv === "function") {
            kv = global.CcsIdb.createKv(DB_NAME, DB_VER, STORE);
        }
        return kv;
    }

    function b64urlFromBuf(buf) {
        const u8 = new Uint8Array(buf);
        let s = "";
        for (let i = 0; i < u8.length; i++) s += String.fromCharCode(u8[i]);
        return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
    }

    function bufFromB64url(s) {
        const pad = s.length % 4 === 0 ? "" : "=".repeat(4 - (s.length % 4));
        const b64 = (s + pad).replace(/-/g, "+").replace(/_/g, "/");
        const bin = atob(b64);
        const u8 = new Uint8Array(bin.length);
        for (let i = 0; i < bin.length; i++) u8[i] = bin.charCodeAt(i);
        return u8.buffer;
    }

    async function generateAndStoreKeyPair() {
        const pair = await crypto.subtle.generateKey(
            { name: "ECDSA", namedCurve: "P-256" },
            false, // private non-extractable
            ["sign", "verify"]
        );
        const publicJwk = await crypto.subtle.exportKey("jwk", pair.publicKey);
        const rec = {
            id: KEY_ID,
            createdAt: new Date().toISOString(),
            publicJwk,
            // CryptoKey objects can be structured-cloned into IDB in modern browsers
            privateKey: pair.privateKey,
            publicKey: pair.publicKey
        };
        const s = store();
        if (s) await s.set(KEY_ID, rec);
        return rec;
    }

    async function getDeviceKeyRecord() {
        const s = store();
        if (!s) {
            // No IDB — ephemeral key for this session only
            if (!global._ccsEphemeralSealKey) {
                const pair = await crypto.subtle.generateKey(
                    { name: "ECDSA", namedCurve: "P-256" },
                    false,
                    ["sign", "verify"]
                );
                global._ccsEphemeralSealKey = {
                    id: KEY_ID + "-ephemeral",
                    createdAt: new Date().toISOString(),
                    publicJwk: await crypto.subtle.exportKey("jwk", pair.publicKey),
                    privateKey: pair.privateKey,
                    publicKey: pair.publicKey,
                    ephemeral: true
                };
            }
            return global._ccsEphemeralSealKey;
        }
        let rec = await s.get(KEY_ID);
        if (!rec || !rec.privateKey) {
            rec = await generateAndStoreKeyPair();
        }
        return rec;
    }

    /**
     * Canonical bytes we sign — seal fields that are commitments, not the signature itself.
     */
    function sealCommitmentBytes(seal) {
        if (!seal || typeof seal !== "object") return new Uint8Array(0);
        const body = {
            v: seal.v,
            alg: seal.alg,
            contentHash: seal.contentHash,
            schemaHash: seal.schemaHash,
            originHash: seal.originHash,
            originAt: seal.originAt,
            sealedAt: seal.sealedAt,
            purpose: seal.purpose,
            flattened: seal.flattened
        };
        const str = (global.CcsProjectState && global.CcsProjectState.stableStringify)
            ? global.CcsProjectState.stableStringify(body)
            : JSON.stringify(body);
        return new TextEncoder().encode(str);
    }

    /**
     * Sign seal commitments; mutates seal with .sig { alg, publicJwk, signature, keyId, signedAt }
     */
    async function signSeal(seal) {
        if (!seal) return seal;
        try {
            const rec = await getDeviceKeyRecord();
            const data = sealCommitmentBytes(seal);
            const sigBuf = await crypto.subtle.sign(
                { name: "ECDSA", hash: "SHA-256" },
                rec.privateKey,
                data
            );
            seal.sig = {
                alg: "ECDSA-P256-SHA256",
                keyId: rec.id || KEY_ID,
                publicJwk: rec.publicJwk,
                signature: b64urlFromBuf(sigBuf),
                signedAt: new Date().toISOString(),
                ephemeral: !!rec.ephemeral
            };
        } catch (e) {
            console.warn("[project-crypto] signSeal failed", e);
            seal.sig = { error: String(e && e.message || e) };
        }
        return seal;
    }

    /**
     * Verify seal.sig using embedded publicJwk.
     */
    async function verifySealSignature(seal) {
        const out = { ok: false, message: "No signature" };
        if (!seal || !seal.sig || !seal.sig.signature || !seal.sig.publicJwk) {
            return out;
        }
        try {
            const pub = await crypto.subtle.importKey(
                "jwk",
                seal.sig.publicJwk,
                { name: "ECDSA", namedCurve: "P-256" },
                true,
                ["verify"]
            );
            const data = sealCommitmentBytes(seal);
            const sig = bufFromB64url(seal.sig.signature);
            const ok = await crypto.subtle.verify(
                { name: "ECDSA", hash: "SHA-256" },
                pub,
                sig,
                data
            );
            out.ok = ok;
            out.message = ok
                ? "Device seal signature valid (ECDSA P-256)."
                : "Device seal signature INVALID.";
            out.keyId = seal.sig.keyId;
            out.ephemeral = !!seal.sig.ephemeral;
        } catch (e) {
            out.ok = false;
            out.message = "Signature verify error: " + (e && e.message || e);
        }
        return out;
    }

    global.CcsProjectCrypto = {
        getDeviceKeyRecord,
        signSeal,
        verifySealSignature,
        sealCommitmentBytes
    };
})(typeof window !== "undefined" ? window : globalThis);
