import { toDispatchStampRecord, } from './dispatch-report.js';
const ACTIONS = new Set(['implementation', 'fix', 'review']);
const ROLES = new Set(['implementer', 'fix', 'reviewer']);
const PROVENANCES = new Set([
    'declared',
    'observed',
    'inferred',
    'unknown',
]);
const FIELD_ORDER = [
    ['scope', 'scope'],
    ['action', 'action'],
    ['role', 'role'],
    ['producer', 'producer'],
    ['provenance', 'provenance'],
    ['modelAxis', 'model_axis'],
    ['effortAxis', 'effort_axis'],
    ['dispatchPolicy', 'dispatch_policy'],
    ['dispatchCeiling', 'dispatch_ceiling'],
    ['target', 'target'],
];
function singleToken(value, fallback) {
    const trimmed = value?.trim() ?? '';
    return trimmed.length > 0 && /^\S+$/.test(trimmed) ? trimmed : fallback;
}
function normalizeAction(value) {
    return ACTIONS.has(value)
        ? value
        : undefined;
}
function normalizeRole(value) {
    return ROLES.has(value) ? value : undefined;
}
function normalizeProvenance(value) {
    return PROVENANCES.has(value)
        ? value
        : 'unknown';
}
function inferRole(action) {
    if (action === 'review') {
        return 'reviewer';
    }
    if (action === 'fix') {
        return 'fix';
    }
    return 'implementer';
}
export function formatDispatchStamp(value) {
    const record = 'schemaVersion' in value ? toDispatchStampRecord(value) : value;
    const normalized = {
        scope: singleToken(record.scope, 'unknown'),
        action: record.action,
        role: record.role,
        producer: singleToken(record.producer, 'unknown'),
        provenance: normalizeProvenance(record.provenance),
        modelAxis: singleToken(record.modelAxis, 'unknown'),
        effortAxis: singleToken(record.effortAxis, 'unknown'),
        dispatchPolicy: singleToken(record.dispatchPolicy, 'unknown'),
        dispatchCeiling: singleToken(record.dispatchCeiling, 'none'),
        target: singleToken(record.target, 'unknown'),
    };
    return `Dispatch: ${FIELD_ORDER.map(([property, key]) => `${key}=${normalized[property]}`).join(' ')}`;
}
function tokenMap(tokens) {
    const fields = new Map();
    for (const token of tokens) {
        const sanitized = token.replace(/,$/, '');
        const match = sanitized.match(/^([a-z_]+)=(.+)$/);
        const key = match?.[1];
        const value = match?.[2];
        if (key && value) {
            fields.set(key, value);
        }
    }
    return fields;
}
function dispatchPayload(line) {
    const match = line.match(/\bDispatch:\s*(.+)$/);
    return match?.[1]?.trim();
}
function warn(options, lineNumber, reason) {
    options.onWarning?.(`Skipped malformed Dispatch line ${lineNumber}: ${reason}`);
}
function parseModernStamp(line, lineNumber, fields, options) {
    const action = normalizeAction(fields.get('action'));
    if (!action) {
        warn(options, lineNumber, 'missing or invalid action');
        return undefined;
    }
    const role = normalizeRole(fields.get('role'));
    if (!role) {
        warn(options, lineNumber, 'missing or invalid role');
        return undefined;
    }
    if (role !== inferRole(action)) {
        warn(options, lineNumber, `incompatible action/role pair ${action}/${role}`);
        return undefined;
    }
    const scope = singleToken(fields.get('scope'), '');
    if (!scope) {
        warn(options, lineNumber, 'missing scope');
        return undefined;
    }
    return {
        scope,
        action,
        role,
        producer: singleToken(fields.get('producer'), 'unknown'),
        provenance: normalizeProvenance(fields.get('provenance')),
        modelAxis: singleToken(fields.get('model_axis'), 'unknown'),
        effortAxis: singleToken(fields.get('effort_axis'), 'unknown'),
        dispatchPolicy: singleToken(fields.get('dispatch_policy'), 'unknown'),
        dispatchCeiling: singleToken(fields.get('dispatch_ceiling'), 'none'),
        target: singleToken(fields.get('target'), 'unknown'),
        line,
        lineNumber,
        legacy: false,
    };
}
function parseLegacyStamp(line, lineNumber, tokens, fields, options) {
    const scope = singleToken(tokens[0], '');
    const action = normalizeAction(tokens[1]);
    if (!scope || !action) {
        warn(options, lineNumber, 'missing legacy scope/action');
        return undefined;
    }
    return {
        scope,
        action,
        role: inferRole(action),
        producer: 'unknown',
        provenance: 'unknown',
        modelAxis: singleToken(fields.get('model_axis'), 'unknown'),
        effortAxis: singleToken(fields.get('effort_axis'), 'unknown'),
        dispatchPolicy: singleToken(fields.get('dispatch_policy'), 'unknown'),
        dispatchCeiling: singleToken(fields.get('dispatch_ceiling'), 'none'),
        target: singleToken(fields.get('target'), 'unknown'),
        line,
        lineNumber,
        legacy: true,
    };
}
export function parseDispatchStamps(markdown, options = {}) {
    const stamps = [];
    const lines = markdown.split(/\r?\n/);
    lines.forEach((line, index) => {
        const payload = dispatchPayload(line);
        if (!payload) {
            return;
        }
        const tokens = payload.split(/\s+/);
        const fields = tokenMap(tokens);
        const stamp = fields.has('scope')
            ? parseModernStamp(line, index + 1, fields, options)
            : parseLegacyStamp(line, index + 1, tokens, fields, options);
        if (stamp) {
            stamps.push(stamp);
        }
    });
    return stamps;
}
export function getProducerIdentitiesByScope(markdown, options = {}) {
    const grouped = {};
    for (const stamp of parseDispatchStamps(markdown, options)) {
        const identities = grouped[stamp.scope] ?? [];
        identities.push({
            scope: stamp.scope,
            action: stamp.action,
            role: stamp.role,
            producer: stamp.producer,
            provenance: stamp.provenance,
            target: stamp.target,
        });
        grouped[stamp.scope] = identities;
    }
    return grouped;
}
