export type LeaseLifecycleRunner = (args: string[]) => boolean;

type LifecycleEvent = {
  reason?: unknown;
  toolName?: unknown;
};

type LifecycleHandler = (
  event: LifecycleEvent,
  context: Record<string, unknown>,
) => unknown | Promise<unknown>;

export interface LeaseLifecyclePiApi {
  on(event: string, handler: LifecycleHandler): void;
}

const ROLLOVER_REASONS = new Set(['reload', 'new', 'resume', 'fork']);

function eventReason(event: LifecycleEvent): string {
  return typeof event.reason === 'string' && event.reason.length > 0 ? event.reason : 'unknown';
}

/**
 * Register redundant Pi compaction observers and same-PID generation rollover.
 *
 * A failed pre-compaction observer cancels compaction. A failed post-compaction
 * observer or generation rollover locally blocks later tools in addition to the
 * broker-backed all-tools gate.
 */
export function registerLeaseLifecycleHooks(
  pi: LeaseLifecyclePiApi,
  runRevoker: LeaseLifecycleRunner,
): void {
  let postCompactReason: string | null = null;
  let postCompactFailure = false;
  let rolloverFailure = false;

  pi.on('session_before_compact', async (event) => {
    const reason = eventReason(event);
    const revoked = runRevoker([
      '--runtime',
      'pi',
      '--reason',
      `pi-session-before-compact:${reason}`,
    ]);
    if (!revoked) return { cancel: true };
    return undefined;
  });

  pi.on('session_compact', async (event) => {
    postCompactReason = eventReason(event);
  });

  pi.on('context', async () => {
    if (postCompactReason === null) return undefined;
    const reason = postCompactReason;
    const revoked = runRevoker([
      '--runtime',
      'pi',
      '--reason',
      `pi-context-after-compact:${reason}`,
    ]);
    if (revoked) {
      postCompactReason = null;
      postCompactFailure = false;
    } else {
      postCompactFailure = true;
    }
    return undefined;
  });

  pi.on('session_start', async (event) => {
    const reason = eventReason(event);
    if (!ROLLOVER_REASONS.has(reason)) return undefined;
    const revoked = runRevoker([
      '--runtime',
      'pi',
      '--reason',
      `pi-session-start:${reason}`,
      '--bump-generation',
    ]);
    rolloverFailure = !revoked;
    return undefined;
  });

  pi.on('tool_call', async () => {
    if (!postCompactFailure && !rolloverFailure) return undefined;
    return {
      block: true,
      reason: 'BLOCKED: Mosaic lease lifecycle revoke failed; runtime remains UNVERIFIED.',
    };
  });
}
