/**
 * That a write Piggy made can be told from one a person typed.
 *
 * The product's safety argument is that nothing lands until a human presses
 * Apply. That argument is only checkable after the fact if the record surfaces
 * can say which rows came from the agent — and until this landed they could
 * not: an approved write read as hand-typed in the account timeline while the
 * seeded row beneath it said "· by piggy".
 *
 * Piggy signs a row in two places and the payload has to answer for both, so
 * both are asserted here, along with the two rows that must NOT be claimed:
 * a person's own entry, and a Slack sync that also carries an external id.
 */
import assert from 'node:assert/strict';
import test from 'node:test';
import {
  PIGGY_EXTERNAL_ID_PREFIX,
  toActivityPayload,
  type ActivityRow,
} from '../src/lib/activity-payload';
import { runMode } from '../src/services/piggy-activity';

function activity(overrides: Partial<ActivityRow> = {}): ActivityRow {
  return {
    id: '60000000-0000-4000-8000-000000000001',
    type: 'call',
    subject: 'Call with DEMO — Northwind Robotics about extending the H200 block',
    body: null,
    accountId: '10000000-0000-4000-8000-00000000000a',
    contactId: null,
    demandDealId: null,
    supplyDealId: null,
    actorAgent: null,
    externalId: null,
    meta: null,
    occurredAt: new Date('2026-08-13T09:00:00.000Z'),
    ...overrides,
  };
}

test('an activity Piggy logged is attributed to Piggy', () => {
  // `pig_log_activity`'s row is its own audit event, so the provenance rides on
  // the external id — which is also what stops a retried tool call logging the
  // same conversation twice.
  const payload = toActivityPayload(
    activity({ externalId: `${PIGGY_EXTERNAL_ID_PREFIX}d016db18-a6eb-4857-9cb5-cff3d58c78d0` }),
  );
  assert.equal(payload.actorAgent, 'piggy');
  // Still on the wire, because a record surface may want to draw the row Piggy
  // logged differently from the audit of a record Piggy changed.
  assert.ok(payload.externalId?.startsWith(PIGGY_EXTERNAL_ID_PREFIX));
});

test('the audit of a record Piggy changed is attributed to Piggy', () => {
  // Every write tool other than `pig_log_activity` stamps the audit row's meta
  // instead, because the mutation convention writes that row, not the tool.
  const payload = toActivityPayload(activity({ meta: { actorAgent: 'piggy', piggyTool: 'pig_update_deal_stage' } }));
  assert.equal(payload.actorAgent, 'piggy');
});

test('a person’s own entry claims no agent', () => {
  assert.equal(toActivityPayload(activity()).actorAgent, null);
});

test('a synced entry is not mistaken for Piggy’s', () => {
  // Slack and Buzz carry external ids too. Attributing their rows to the agent
  // would put words in Piggy's mouth on the surface people audit it from.
  const payload = toActivityPayload(activity({ externalId: 'slack:C09QT/1755082800.123' }));
  assert.equal(payload.actorAgent, null);
});

test('a stored agent stamp still wins', () => {
  // An API key really did authenticate as an agent; the derivation must not
  // overwrite what the column already recorded.
  assert.equal(toActivityPayload(activity({ actorAgent: 'agent' })).actorAgent, 'agent');
});

test('meta that is not an object cannot break the timeline', () => {
  // `meta` is free-form JSON written by every mutation in the product.
  const hostile = { meta: ['piggy'] as unknown as Record<string, unknown> };
  assert.equal(toActivityPayload(activity(hostile)).actorAgent, null);
});

test('the payload carries no internal blob', () => {
  const payload = toActivityPayload(activity({ meta: { slackPermalink: 'https://…' } }));
  assert.equal('meta' in payload, false);
  assert.equal(payload.occurredAt, '2026-08-13T09:00:00.000Z');
});

test('a run reports the mode it was allowed to run in', () => {
  assert.equal(runMode({ surface: 'chat', mode: 'auto' }), 'auto');
  assert.equal(runMode({ surface: 'chat', mode: 'read_only' }), 'read_only');
});

test('a run that recorded no mode reports none, rather than the safe one', () => {
  // A queued task has no mode, and neither do the chat turns written before the
  // relay stamped it. Defaulting those to `read_only` would put a claim in the
  // ledger that nobody made.
  assert.equal(runMode(null), null);
  assert.equal(runMode({ surface: 'chat' }), null);
  assert.equal(runMode({ mode: 'yolo' }), null);
  assert.equal(runMode({ mode: 42 }), null);
});
