/**
 * Attribution backfill for existing issue and conversation_message entities.
 *
 * Scans all `issue` entities and linked `conversation_message` entities that
 * have an `author` field (GitHub login) but whose provenance does not contain
 * `external_actor`. For each, writes a `correct` observation that adds
 * structured `github_actor` fields and stamps `external_actor` in provenance
 * with `verified_via: "claim"`.
 *
 * Invoked via `neotoma issues attribution-backfill`.
 * Idempotent: rows that already carry `external_actor` in provenance are skipped.
 */
import { runWithExternalActor } from "../request_context.js";
function entityPrimaryId(row) {
    const entityId = typeof row.entity_id === "string" ? row.entity_id : "";
    if (entityId)
        return entityId;
    const id = typeof row.id === "string" ? row.id : "";
    if (id)
        return id;
    throw new Error("Entity row missing entity_id");
}
export async function runAttributionBackfill(ops) {
    const result = {
        issues_updated: 0,
        messages_updated: 0,
        skipped: 0,
        errors: [],
    };
    const issuesResponse = (await ops.retrieveEntities({
        entity_type: "issue",
        limit: 500,
    }));
    const issues = issuesResponse?.entities ?? [];
    for (const issue of issues) {
        try {
            const provenance = issue.provenance ?? {};
            if (provenance.external_actor) {
                result.skipped++;
                continue;
            }
            const author = issue.snapshot?.author;
            if (!author || author === "unknown" || author === "local") {
                result.skipped++;
                continue;
            }
            const actor = {
                provider: "github",
                login: author,
                id: 0,
                type: "User",
                verified_via: "claim",
            };
            (await runWithExternalActor(actor, () => ops.store({
                entities: [
                    {
                        entity_type: "issue",
                        ...issue.snapshot,
                        github_actor: { login: author, id: 0, type: "User" },
                    },
                ],
                idempotency_key: `backfill-issue-actor-${issue.entity_id}`,
            })));
            result.issues_updated++;
            const relatedResponse = (await ops.retrieveRelatedEntities({
                entity_id: issue.entity_id,
                relationship_types: ["REFERS_TO"],
                direction: "outbound",
            }));
            const conversation = relatedResponse?.entities?.find((e) => e.entity_type === "conversation");
            if (!conversation)
                continue;
            const partsResponse = (await ops.retrieveRelatedEntities({
                entity_id: entityPrimaryId(conversation),
                relationship_types: ["PART_OF"],
                direction: "inbound",
            }));
            const messages = (partsResponse?.entities ?? []).filter((e) => e.entity_type === "conversation_message");
            for (const msg of messages) {
                if (msg.provenance?.external_actor)
                    continue;
                const msgAuthor = msg.snapshot?.author;
                if (!msgAuthor || msgAuthor === "unknown" || msgAuthor === "local")
                    continue;
                const msgActor = {
                    provider: "github",
                    login: msgAuthor,
                    id: 0,
                    type: "User",
                    verified_via: "claim",
                };
                (await runWithExternalActor(msgActor, () => ops.store({
                    entities: [
                        {
                            entity_type: "conversation_message",
                            ...msg.snapshot,
                            github_actor: { login: msgAuthor, id: 0, type: "User" },
                        },
                    ],
                    idempotency_key: `backfill-msg-actor-${msg.entity_id}`,
                })));
                result.messages_updated++;
            }
        }
        catch (err) {
            result.errors.push(`Issue ${issue.entity_id}: ${err.message}`);
        }
    }
    return result;
}
//# sourceMappingURL=attribution_backfill.js.map