/**
 * The agent ledger, over HTTP.
 *
 * One GET. Everything interesting about it is in the service; what belongs here
 * is the gate.
 *
 * `book:read` is the floor, and it is deliberately NOT `economics:read` even
 * though this endpoint returns money. The figures are what PIG spent on
 * inference on the caller's behalf — not supplier cost, not break-even, not
 * margin — and a research lead who may not see the cost book must still be able
 * to see what their own questions cost, or the audit surface is only auditable
 * by the people who least need it. The row in READ_RULES is what denies the
 * stranger, the write-only credential and the person on no team; who sees whose
 * runs is settled inside the service by the ownership predicate.
 *
 * The second gate is `withoutOtherPeoplesWords` below, and it is why this file
 * is longer than one handler. See the note on it.
 */
import type { Database } from '@pig/db';
import { Hono } from 'hono';
import type { ApiEnv } from '../lib/mutation';
import { PiggyActivityService, type PiggyActivityOverview } from '../services/piggy-activity';

/** Spelled once, so the READ_RULES row and the mount cannot drift apart. */
export const PIGGY_ACTIVITY_PATH = '/api/piggy/activity';

/**
 * What another member's turn is called in an administrator's ledger.
 *
 * Deliberately says whose it was and nothing about what it asked. The row still
 * carries the name, the model, the tokens, the cost, the status and the error,
 * because those are what an audit is for.
 */
export const PIGGY_WITHHELD_LABEL = 'Another member’s turn';

/**
 * Take the words out of the rows that are not the caller's own.
 *
 * The policy, stated once, because two files were quietly contradicting each
 * other about it:
 *
 *   **Cost and outcome are the company's record. The words are the person's.**
 *
 * `piggy-conversations.ts` already says so at the top and enforces it with a
 * predicate that a platform admin is no exception to. `PiggyActivityService`
 * says the same thing in its header — and then returned `label`, which is the
 * user's question cut to 180 characters, and `summary`, which is the first line
 * of Piggy's answer, for every run in the workspace once the caller was an
 * admin. So the ledger was a keyhole into exactly the material the transcript
 * store refuses to hand over, and while `piggy_messages` was never written it
 * was the ONLY copy of a conversation anyone could reach.
 *
 * Now that transcripts persist properly the contradiction has no excuse left,
 * and it is settled the way the conversation store settles it. An admin keeps
 * everything they need — what ran, whose it was, whether it failed, what it
 * cost, how long it took — and loses the two fields that are somebody's private
 * questions about the book. An admin reading their OWN runs sees them in full,
 * as does everybody else, because `runs` scoped to a caller returns no
 * `principal` on their own rows: that field is populated only when the run
 * belongs to somebody else, which makes it the exact signal this needs.
 *
 * It sits in the route rather than the service on the reasoning this file
 * opened with — the service computes the ledger, the route is the gate — and
 * because `overview` has one caller. Should a second appear, this moves down.
 *
 * One case is deliberately left open, and is written down rather than left to
 * be discovered. `agent_runs.principal_user_id` is `ON DELETE set null`, so a
 * departed colleague's runs survive with no owner, and the service reports an
 * ownerless run exactly as it reports the caller's own: `principal: null`. To
 * an administrator those two are indistinguishable from here, so a leaver's
 * questions stay legible while a current colleague's do not. Closing it needs
 * `PiggyRunSummary` to say whose a run is not, rather than only when it is
 * somebody else's — a change in the service, and the wrong thing to guess at
 * from the gate. The retention question underneath it is larger still: the
 * ledger keeps `input.message` after the transcript it belonged to has been
 * cascaded away with its author.
 */
export function withoutOtherPeoplesWords(overview: PiggyActivityOverview): PiggyActivityOverview {
  return {
    ...overview,
    runs: overview.runs.map((run) =>
      run.principal
        ? { ...run, label: PIGGY_WITHHELD_LABEL, summary: null }
        : run,
    ),
  };
}

export function createPiggyActivityRoutes(db: Database): Hono<ApiEnv> {
  const routes = new Hono<ApiEnv>();
  const activity = new PiggyActivityService(db);

  /*
   * Spelled as a literal, not as the constant above.
   *
   * `read-governance.test.ts` finds every read by grepping the route sources
   * for a get call with an /api path quoted inside it, so a path assembled
   * from a constant is one the governance check cannot see — an ungoverned
   * read that looks governed,
   * which is the precise failure that test exists to catch. `satisfies` keeps
   * the literal and the constant from drifting: change one and this stops
   * compiling.
   */
  routes.get('/api/piggy/activity' satisfies typeof PIGGY_ACTIVITY_PATH, async (c) => {
    return c.json(withoutOtherPeoplesWords(await activity.overview(c.get('principal'))));
  });

  return routes;
}
