/**
 * Evaluates trust for graph writers using signed evidence.
 *
 * Reads trust record chains via TrustChainPort, builds trust state,
 * evaluates writer policies, and returns TrustAssessment.
 *
 * @module domain/services/audit/TrustEvaluationService
 */
import { evaluateWriters } from "../../trust/TrustEvaluator.js";
import { TrustAssessment } from "../../trust/TrustAssessment.js";
import { buildState } from "../../trust/TrustStateBuilder.js";
import { TRUST_REASON_CODES } from "../../trust/reasonCodes.js";
import defaultTrustCrypto from "../../utils/defaultTrustCrypto.js";
function isNonEmptyStr(v) {
    return typeof v === 'string' && v.length > 0;
}
function resolveTrustSource(options) {
    const pinned = isNonEmptyStr(options.pin);
    return {
        status: resolveField(options.status, pinned ? 'pinned' : 'configured'),
        source: resolveField(options.source, pinned ? 'pinned' : 'ref'),
        sourceDetail: options.sourceDetail ?? options.pin ?? null,
    };
}
function resolveField(explicit, fallback) {
    return isNonEmptyStr(explicit) ? explicit : fallback;
}
function buildFailureExplanations(writers, reasonCode, reason) {
    if (writers.length > 0) {
        return writers.map((writerId) => ({ writerId, trusted: false, reasonCode, reason }));
    }
    return [{ writerId: '*', trusted: false, reasonCode, reason }];
}
function buildFailureAssessment(params) {
    const evaluatedWriters = [...(params.writerIds ?? [])].sort();
    // Pre-existing: source values ('pinned', 'configured') don't match
    // TrustSource union. Tracked as bad-code/TRUST_source-enum-mismatch.
    return new TrustAssessment({
        status: params.status,
        source: params.source,
        sourceDetail: params.sourceDetail,
        evaluatedWriters, untrustedWriters: evaluatedWriters,
        explanations: buildFailureExplanations(evaluatedWriters, params.reasonCode, params.reason),
        evidenceSummary: {
            recordsScanned: params.recordsScanned ?? 0,
            activeKeys: 0, revokedKeys: 0, activeBindings: 0, revokedBindings: 0,
        },
    });
}
/**
 * Evaluates trust for graph writers using the trust record chain.
 */
export default class TrustEvaluationService {
    _trustChain;
    _trustCrypto;
    _listWriterIds;
    constructor(opts) {
        this._trustChain = opts.trustChain ?? null;
        this._trustCrypto = opts.trustCrypto ?? defaultTrustCrypto;
        this._listWriterIds = opts.listWriterIds;
    }
    async evaluateTrust(graphName, options = {}) {
        const resolved = resolveTrustSource(options);
        const earlyExit = this._checkPreconditions(resolved);
        if (earlyExit) {
            return earlyExit;
        }
        const readResult = await this._readRecords(graphName, options.pin);
        if (readResult.error !== null) {
            return this._failWith(resolved, `Trust chain read failed: ${readResult.error}`);
        }
        if (readResult.records.length === 0) {
            return this._buildNotConfiguredAssessment();
        }
        return await this._evaluateWithState(readResult.records, graphName, {
            options, ...resolved,
        });
    }
    _checkPreconditions(resolved) {
        if (!this._trustChain) {
            return this._failWith(resolved, 'Trust chain port not configured');
        }
        return null;
    }
    _failWith(resolved, reason) {
        return buildFailureAssessment({
            status: 'error', source: resolved.source, sourceDetail: resolved.sourceDetail,
            reasonCode: TRUST_REASON_CODES.TRUST_RECORD_CHAIN_INVALID, reason,
        });
    }
    async _readRecords(graphName, pin) {
        const tip = isNonEmptyStr(pin) ? pin : undefined;
        const records = [];
        try {
            for await (const record of this._trustChain.readRecords(graphName, tip)) {
                records.push(record);
            }
            return { records, error: null };
        }
        catch (err) {
            return { records: [], error: err instanceof Error ? err.message : String(err) };
        }
    }
    _buildNotConfiguredAssessment() {
        return new TrustAssessment({
            status: 'not_configured',
            source: 'none',
            sourceDetail: null,
            evaluatedWriters: [],
            untrustedWriters: [],
            explanations: [],
            evidenceSummary: {
                recordsScanned: 0, activeKeys: 0, revokedKeys: 0,
                activeBindings: 0, revokedBindings: 0,
            },
        });
    }
    async _evaluateWithState(records, graphName, ctx) {
        const trustState = await this._buildTrustState(records);
        const writerIds = ctx.options.writerIds
            ? [...ctx.options.writerIds]
            : await this._listWriterIds(graphName);
        const policy = {
            schemaVersion: 1,
            mode: (ctx.options.mode ?? 'warn'),
            writerPolicy: 'all_writers_must_be_trusted',
        };
        const assessment = evaluateWriters(writerIds, trustState, policy);
        return new TrustAssessment({
            ...assessment.trust,
            status: assessment.trust.status === 'error' ? 'error' : ctx.status,
            source: ctx.source,
            sourceDetail: ctx.sourceDetail,
        });
    }
    async _buildTrustState(records) {
        return await buildState(records, {
            signatureVerifier: (record, publicKeyBase64) => this._trustCrypto.verifySignature({
                algorithm: record.signature.alg,
                publicKeyBase64,
                signatureBase64: record.signature.sig,
                payload: record.signaturePayload,
            }),
            computeKeyFingerprint: (publicKeyBase64) => this._trustCrypto.computeKeyFingerprint(publicKeyBase64),
        });
    }
}
