/**
 * GDPR Right to Erasure (Article 17).
 *
 * Cascade-deletes all data held on a specific contact across Neo4j stores:
 *   1. Messages within attributed Conversations (via Person.sessionId)
 *   2. Conversations left empty after message removal
 *   3. Emails matching the contact's email (fromAddress or toAddresses)
 *   4. AccessGrants via (Person)-[:HAS_ACCESS]->(AccessGrant)
 *   5. Visitor-graph data (Task 357):
 *      - :Session reachable via (Person)-[:VISITED]->(Session)
 *      - all :HAS_EVENT children (PageView/Click/ScrollMilestone)
 *      - :AnonVisitor via (Person)-[:OWNS_VISITOR]->(AnonVisitor)
 *      :Page and :Listing are content metadata and are intentionally preserved.
 *   6. Person node itself (DETACH DELETE)
 *
 * Requires `confirm: true` to execute. Without it, returns a preview
 * of what would be deleted (counts per node type) but does not delete.
 */
import { getSession } from "../lib/neo4j.js";
import { resolvePersonMatch } from "../lib/resolve-person.js";
export async function contactErase(params) {
    const { nodeId, email, telephone, confirm = false, accountId } = params;
    const startMs = Date.now();
    const { matchClause, queryParams } = resolvePersonMatch({ nodeId, email, telephone }, accountId, { includeTrashed: true });
    const session = getSession();
    try {
        // Step 1: Resolve Person node and gather its properties
        const personResult = await session.run(`${matchClause}
       RETURN elementId(p) AS personNodeId, p.email AS personEmail,
              p.telephone AS personPhone, p.sessionId AS sessionId`, queryParams);
        if (personResult.records.length === 0) {
            const identifier = email ?? telephone ?? nodeId ?? "unknown";
            console.error(JSON.stringify({
                tool: "contact-erase",
                status: "not-found",
                identifier,
                latencyMs: Date.now() - startMs,
            }));
            throw new Error("Contact not found");
        }
        const record = personResult.records[0];
        const personNodeId = record.get("personNodeId");
        const personEmail = record.get("personEmail");
        const personPhone = record.get("personPhone");
        const sessionId = record.get("sessionId");
        const identifier = personEmail ?? personPhone ?? nodeId ?? "unknown";
        // Step 2: Count what would be deleted
        const counts = {
            messages: 0,
            conversations: 0,
            emails: 0,
            accessGrants: 0,
            visitorSessions: 0,
            visitorEvents: 0,
            anonVisitors: 0,
            person: 1,
        };
        // Count visitor-graph data — Sessions, events, AnonVisitors (Task 357)
        {
            const visitorCounts = await session.run(`MATCH (p:Person) WHERE elementId(p) = $personNodeId
         OPTIONAL MATCH (p)-[:VISITED]->(vs:Session)
         OPTIONAL MATCH (vs)-[:HAS_EVENT]->(ev)
         OPTIONAL MATCH (p)-[:OWNS_VISITOR]->(av:AnonVisitor)
         RETURN count(DISTINCT vs) AS visitorSessions,
                count(DISTINCT ev) AS visitorEvents,
                count(DISTINCT av) AS anonVisitors`, { personNodeId });
            if (visitorCounts.records.length > 0) {
                const row = visitorCounts.records[0];
                counts.visitorSessions = Number(row.get("visitorSessions")?.valueOf?.() ?? row.get("visitorSessions") ?? 0);
                counts.visitorEvents = Number(row.get("visitorEvents")?.valueOf?.() ?? row.get("visitorEvents") ?? 0);
                counts.anonVisitors = Number(row.get("anonVisitors")?.valueOf?.() ?? row.get("anonVisitors") ?? 0);
            }
        }
        // Count Messages + Conversations via sessionId
        if (sessionId) {
            const convCountResult = await session.run(`MATCH (c:Conversation {sessionId: $sessionId, accountId: $accountId})
         OPTIONAL MATCH (m:Message)-[:PART_OF]->(c)
         RETURN count(DISTINCT c) AS convCount, count(m) AS msgCount`, { sessionId, accountId });
            if (convCountResult.records.length > 0) {
                counts.conversations = convCountResult.records[0].get("convCount").low ?? 0;
                counts.messages = convCountResult.records[0].get("msgCount").low ?? 0;
            }
        }
        // Count email-source :KnowledgeDocument threads via Person FROM/TO/CC
        // edges. Task 321 retired :Email; threads now live as
        // :KnowledgeDocument {source:'email'} linked to :Person via address edges.
        if (personEmail) {
            const emailCountResult = await session.run(`MATCH (kd:KnowledgeDocument {source: 'email', accountId: $accountId})
         MATCH (p:Person {email: $personEmail})
         WHERE (kd)-[:FROM]->(p) OR (kd)-[:TO]->(p) OR (kd)-[:CC]->(p)
         RETURN count(DISTINCT kd) AS emailCount`, { personEmail, accountId });
            if (emailCountResult.records.length > 0) {
                counts.emails = emailCountResult.records[0].get("emailCount").low ?? 0;
            }
        }
        // Count AccessGrants
        const grantCountResult = await session.run(`MATCH (p:Person)-[:HAS_ACCESS]->(g:AccessGrant)
       WHERE elementId(p) = $personNodeId
       RETURN count(g) AS grantCount`, { personNodeId });
        if (grantCountResult.records.length > 0) {
            counts.accessGrants = grantCountResult.records[0].get("grantCount").low ?? 0;
        }
        // If not confirmed, return preview only
        if (!confirm) {
            console.error(JSON.stringify({
                tool: "contact-erase",
                status: "preview",
                identifier,
                confirmed: false,
                counts,
                latencyMs: Date.now() - startMs,
            }));
            return { confirmed: false, identifier, counts };
        }
        // Step 3: Execute cascade deletion in a single transaction
        const tx = session.beginTransaction();
        try {
            // 3a. Delete Messages in attributed Conversations
            if (sessionId) {
                await tx.run(`MATCH (m:Message)-[:PART_OF]->(c:Conversation {sessionId: $sessionId, accountId: $accountId})
           DETACH DELETE m`, { sessionId, accountId });
                // 3b. Delete now-empty Conversations
                await tx.run(`MATCH (c:Conversation {sessionId: $sessionId, accountId: $accountId})
           WHERE NOT EXISTS { MATCH (m:Message)-[:PART_OF]->(c) }
           DETACH DELETE c`, { sessionId, accountId });
            }
            // 3c. Delete email-source :KnowledgeDocument threads that mention this
            // person via FROM/TO/CC edges. Task 321 retired :Email — every email
            // thread is one :KnowledgeDocument {source:'email'}.
            if (personEmail) {
                await tx.run(`MATCH (kd:KnowledgeDocument {source: 'email', accountId: $accountId})
           MATCH (p:Person {email: $personEmail})
           WHERE (kd)-[:FROM]->(p) OR (kd)-[:TO]->(p) OR (kd)-[:CC]->(p)
           DETACH DELETE kd`, { personEmail, accountId });
            }
            // 3d. Delete AccessGrants via relationship
            await tx.run(`MATCH (p:Person)-[:HAS_ACCESS]->(g:AccessGrant)
         WHERE elementId(p) = $personNodeId
         DELETE g`, { personNodeId });
            // 3d-bis. Visitor-graph cascade (Task 357). :Page and :Listing are
            // content metadata and intentionally preserved — they have no PII.
            await tx.run(`MATCH (p:Person) WHERE elementId(p) = $personNodeId
         OPTIONAL MATCH (p)-[:VISITED]->(vs:Session)
         OPTIONAL MATCH (vs)-[:HAS_EVENT]->(ev)
         DETACH DELETE ev, vs`, { personNodeId });
            await tx.run(`MATCH (p:Person)-[:OWNS_VISITOR]->(av:AnonVisitor)
         WHERE elementId(p) = $personNodeId
         DETACH DELETE av`, { personNodeId });
            // 3e. Delete Person node (DETACH DELETE to remove any remaining relationships)
            await tx.run(`MATCH (p:Person) WHERE elementId(p) = $personNodeId
         DETACH DELETE p`, { personNodeId });
            await tx.commit();
        }
        catch (err) {
            await tx.rollback();
            console.error(JSON.stringify({
                tool: "contact-erase",
                status: "error",
                identifier,
                confirmed: true,
                counts,
                errors: err instanceof Error ? err.message : String(err),
                latencyMs: Date.now() - startMs,
            }));
            throw err;
        }
        console.error(JSON.stringify({
            tool: "contact-erase",
            status: "success",
            identifier,
            confirmed: true,
            counts,
            latencyMs: Date.now() - startMs,
        }));
        return { confirmed: true, identifier, counts };
    }
    finally {
        await session.close();
    }
}
//# sourceMappingURL=contact-erase.js.map