// Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
/**
 * Governance Consumer — Agent-side primitives for reading governed content.
 *
 * Completes the 360 loop:
 *   Publisher: embedGovernance() → HTML with signed block
 *   Agent:     fetchGovernedContent() → verify → check terms → create access receipt
 *
 * The access receipt is cryptographic proof that the agent accessed
 * this content under these terms at this time. This is the evidence
 * trail for compliance monitoring and settlement.
 */
import { createHash } from 'node:crypto';
import { sign, verify, canonicalize, createDID } from '../index.js';
import { parseGovernanceBlockFromHTML, verifyGovernanceBlock, isUsagePermitted, isGovernanceBlockExpired } from './governance-block.js';
import { parseGovernanceHeaders } from './aps-txt.js';
/**
 * Check governance for HTML content — extracts block from HTML,
 * verifies signature and content hash, checks usage permission.
 */
export function checkHTMLGovernance(html, contentBody, publisherPublicKey, intendedUsage) {
    const block = parseGovernanceBlockFromHTML(html);
    if (!block) {
        return { found: false, source: 'none', block: null, verified: false, usageCheck: null, errors: ['No governance block found in HTML'] };
    }
    const source = html.includes('application/aps-governance+json') ? 'html_script' : 'html_meta';
    const verification = verifyGovernanceBlock(block, contentBody, publisherPublicKey);
    const usageCheck = isUsagePermitted(block, intendedUsage);
    return {
        found: true,
        source,
        block,
        verified: verification.valid,
        usageCheck,
        errors: verification.errors,
    };
}
/**
 * Check governance from HTTP response headers.
 */
export function checkHeaderGovernance(headers, contentBody, publisherPublicKey, intendedUsage) {
    const block = parseGovernanceHeaders(headers);
    if (!block) {
        return { found: false, source: 'none', block: null, verified: false, usageCheck: null, errors: ['No X-APS-Governance header found'] };
    }
    const verification = verifyGovernanceBlock(block, contentBody, publisherPublicKey);
    const usageCheck = isUsagePermitted(block, intendedUsage);
    return { found: true, source: 'http_header', block, verified: verification.valid, usageCheck, errors: verification.errors };
}
// ═══════════════════════════════════════
// Access Receipt — create proof of consumption
// ═══════════════════════════════════════
export function createAccessReceipt(input) {
    const agentDid = createDID(input.agentPublicKey);
    const receiptId = `gar_${createHash('sha256').update(`${agentDid}:${input.block.content_hash}:${Date.now()}`).digest('hex').slice(0, 24)}`;
    const now = new Date().toISOString();
    const receipt = {
        receiptId,
        agent_did: agentDid,
        publisher_did: input.block.source_did,
        content_hash: input.block.content_hash,
        source_url: input.sourceUrl,
        terms_at_access: input.block.terms,
        revocation_policy_at_access: input.block.revocation_policy,
        intended_usage: input.intendedUsage,
        governance_verified: input.governanceVerified,
        accessed_at: now,
    };
    const payload = canonicalize(receipt);
    const signature = sign(payload, input.agentPrivateKey);
    return { ...receipt, signature };
}
export function verifyAccessReceipt(receipt, agentPublicKey) {
    const { signature, ...rest } = receipt;
    const payload = canonicalize(rest);
    return verify(payload, signature, agentPublicKey);
}
/**
 * Execute the full 360 governance loop:
 *
 * 1. Extract governance block from HTML (script tag, meta tag, or headers)
 * 2. Verify signature + content hash + DID consistency
 * 3. Check if intended usage is permitted
 * 4. Create signed access receipt (proof of consumption under terms)
 *
 * This is the function an agent calls on every page it reads.
 */
export function governanceLoop360(input) {
    // Step 1+2: Try HTML first, then headers
    let governance = checkHTMLGovernance(input.html, input.contentBody, input.publisherPublicKey, input.intendedUsage);
    if (!governance.found && input.responseHeaders) {
        governance = checkHeaderGovernance(input.responseHeaders, input.contentBody, input.publisherPublicKey, input.intendedUsage);
    }
    // No governance found — ungoverned content
    if (!governance.found || !governance.block) {
        return {
            governance,
            receipt: null,
            permitted: true, // ungoverned = no restrictions
            summary: `No governance block found at ${input.sourceUrl}. Content is ungoverned.`,
        };
    }
    // Step 3: Check expiry (AV-3: replay prevention)
    if (governance.block && isGovernanceBlockExpired(governance.block)) {
        return {
            governance,
            receipt: null,
            permitted: false,
            summary: `Governance block expired at ${governance.block.expires_at}. Refusing access to prevent replay attack (AV-3).`,
        };
    }
    // Step 4: Check usage
    const permitted = governance.usageCheck?.permitted ?? true;
    // Step 4: Create access receipt
    const receipt = createAccessReceipt({
        agentPublicKey: input.agentPublicKey,
        agentPrivateKey: input.agentPrivateKey,
        block: governance.block,
        sourceUrl: input.sourceUrl,
        intendedUsage: input.intendedUsage,
        governanceVerified: governance.verified,
    });
    const condition = governance.usageCheck?.condition || 'not_specified';
    const summary = [
        `Governance: ${governance.verified ? 'VERIFIED' : 'INVALID'} (${governance.source})`,
        `Publisher: ${governance.block.source_did}`,
        `Usage "${input.intendedUsage}": ${permitted ? 'PERMITTED' : 'RESTRICTED'} (${condition})`,
        `Receipt: ${receipt.receiptId}`,
        governance.verified ? '' : `Errors: ${governance.errors.join('; ')}`,
    ].filter(Boolean).join('\n');
    return { governance, receipt, permitted, summary };
}
//# sourceMappingURL=governance-consumer.js.map