/**
 * tools/matrix-presence-harness/provision.ts
 *
 *   *** DEV harness — LOCAL SANDBOX ONLY. ***
 *
 * The MINIMAL P1 provisioner (RFC-001 §10 P1): its ONLY job is to register a
 * few agent MXIDs, create the fleet presence room, and join the agents. It is
 * deliberately NOT the P2 appservice (no auto-enroll, no room taxonomy, no
 * token minting, no introductions).
 *
 * It reuses the existing, tested `@mosaicstack/appservice` core library
 * (AppserviceIntent: register / createRoom / join over the AS API) rather than
 * reinventing Matrix plumbing.
 *
 * ESM / NodeNext: .js import extensions.
 */
import { AppserviceIntent, type AppserviceConfig } from '@mosaicstack/appservice';

export interface ProvisionOptions {
  homeserverUrl: string;
  serverName: string;
  asToken: string;
  hsToken: string;
  agentSlugs: string[];
  fleetAlias?: string; // localpart, default "mosaic-fleet"
}

export interface ProvisionResult {
  roomId: string;
  senderUserId: string;
  agents: { slug: string; mxid: string }[];
}

/** Register agents, create the fleet presence room, join everyone. Idempotent. */
export async function provision(opts: ProvisionOptions): Promise<ProvisionResult> {
  const cfg: AppserviceConfig = {
    homeserverUrl: opts.homeserverUrl,
    domain: opts.serverName,
    asToken: opts.asToken,
    hsToken: opts.hsToken,
  };
  const intent = new AppserviceIntent(cfg);
  const fleetAlias = opts.fleetAlias ?? 'mosaic-fleet';

  // 1. Register the virtual agent MXIDs (bypasses enable_registration:false).
  const agents = [];
  for (const slug of opts.agentSlugs) {
    const mxid = await intent.ensureRegistered(slug);
    await intent.setDisplayName(slug, `agent ${slug} (DEV)`);
    agents.push({ slug, mxid });
  }

  // 2. Create the single fleet presence room, inviting all agents (RFC-001 §4.6).
  //    Idempotent: if the alias already exists, reuse that room instead.
  let roomId: string;
  try {
    const created = await intent.createRoom({
      name: 'Fleet Presence (DEV)',
      alias: fleetAlias,
      topic: 'RFC-001 P1 — mosaic.presence heartbeats. Who is alive?',
      invite: agents.map((a) => a.mxid),
    });
    roomId = created.roomId;
  } catch (err) {
    // Alias already taken (re-run): resolve the existing room id.
    const aliasFq = `#${fleetAlias}:${opts.serverName}`;
    roomId = await resolveAlias(opts, aliasFq);
    void err;
  }

  // 3. Join every agent into the fleet room (invite + join; idempotent).
  for (const { slug } of agents) {
    await intent.ensureJoined(roomId, slug);
  }

  return { roomId, senderUserId: intent.senderUserId, agents };
}

async function resolveAlias(
  opts: Pick<ProvisionOptions, 'homeserverUrl' | 'asToken'>,
  aliasFq: string,
): Promise<string> {
  const url = `${opts.homeserverUrl.replace(/\/$/, '')}/_matrix/client/v3/directory/room/${encodeURIComponent(aliasFq)}`;
  const res = await fetch(url, { headers: { Authorization: `Bearer ${opts.asToken}` } });
  if (!res.ok) throw new Error(`resolveAlias ${aliasFq} -> ${res.status}`);
  const data = (await res.json()) as { room_id?: string };
  if (!data.room_id) throw new Error(`resolveAlias ${aliasFq} returned no room_id`);
  return data.room_id;
}
