/**
 * @mosaicstack/comms — MACP P1 (presence) types.
 *
 * Implements the presence/liveness slice of RFC-001 §4.5 and the MACP event
 * envelope of RFC-001 §4.2. P1 scope only: presence heartbeat + deterministic
 * liveness. No enrollment, room-taxonomy, token-minting or signed-authorship
 * (those are P2+).
 */

/** The three human-visible liveness states (RFC-001 §4.5). */
export type PresenceStatus = 'online' | 'away' | 'offline';

/**
 * Native Matrix presence EDU states. We still emit these (so Element shows the
 * right dot for humans, RFC-001 §4.5) but they are NOT the authoritative
 * liveness source — the heartbeat is.
 */
export type MatrixPresence = 'online' | 'unavailable' | 'offline';

/**
 * Common MACP event envelope carried in `content` on every custom event
 * (RFC-001 §4.2). P1 uses only the fields the presence heartbeat needs; the
 * `signature` field (gate actions, §4.4) is intentionally absent in P1.
 */
export interface MacpEnvelope {
  macp_version: string;
  macp_type: string;
  agent: {
    mxid: string;
    slug: string;
    harness: string;
  };
  ts: number;
  mission_id?: string;
}

/**
 * `mosaic.presence` heartbeat content (RFC-001 §4.2 "presence" row + §4.5).
 * Carried as an `m.room.message` with `msgtype: "mosaic.presence"` and a
 * human-visible `body` fallback, posted into the fleet presence room.
 */
export interface PresenceHeartbeatContent extends MacpEnvelope {
  macp_type: 'presence';
  msgtype: 'mosaic.presence';
  /** Human-visible fallback so the event renders in a stock client. */
  body: string;
  /** Liveness state the agent asserts about itself. */
  status: PresenceStatus;
  /** Monotonic per-agent sequence number, increments once per beat. */
  seq: number;
  /** The agent's configured heartbeat interval, so readers can reason. */
  interval_ms: number;
}

/**
 * Deterministic liveness policy (RFC-001 §4.5). Defaults per §4.5/§5.3:
 * interval 30s, miss-tolerance 2, dark threshold a policy value (10 min in
 * prod §5; small in dev harness).
 */
export interface LivenessPolicy {
  /** Nominal heartbeat interval in ms. Default 30_000. */
  heartbeatIntervalMs: number;
  /** How many intervals may be missed before "away". Default 2. */
  missTolerance: number;
  /** Age past which an agent is declared offline/dark. Default 600_000. */
  darkThresholdMs: number;
}

/** A single agent's last observed heartbeat, as read from the fleet room. */
export interface HeartbeatObservation {
  slug: string;
  mxid: string;
  /** Wall-clock ms of the last heartbeat seen for this agent. */
  lastSeenTs: number;
  /** Last seq observed (monotonic per agent). */
  lastSeq: number;
  /** The status the agent last asserted about itself. */
  assertedStatus: PresenceStatus;
}

/** Computed liveness for one agent (what a human/watchdog reads). */
export interface AgentLiveness {
  slug: string;
  mxid: string;
  /** Authoritative, heartbeat-derived status. */
  status: PresenceStatus;
  lastSeenTs: number;
  /** now - lastSeenTs, in ms. */
  ageMs: number;
  lastSeq: number;
}

export const DEFAULT_LIVENESS_POLICY: LivenessPolicy = {
  heartbeatIntervalMs: 30_000,
  missTolerance: 2,
  darkThresholdMs: 600_000,
};

export const MACP_VERSION = '1.0';
