import { SUBAGENT_ENV_HINT_KEYS } from "#src/authority/permission-forwarding";
import type { SubagentSessionRegistry } from "#src/authority/subagent-registry";
import type { PathFlavor } from "#src/path/path-flavor";

/**
 * Narrow context for subagent detection — the only session-manager readers
 * {@link isSubagentExecutionContext} and {@link isRegisteredSubagentChild}
 * consume. A full `ExtensionContext` satisfies this structurally.
 */
export interface SubagentDetectionContext {
  sessionManager: {
    getSessionId(): string;
    getSessionDir(): string;
  };
}

export function normalizeFilesystemPath(
  pathValue: string,
  flavor: PathFlavor,
): string {
  return flavor.fold(flavor.impl.normalize(pathValue));
}

/**
 * Return `true` when `ctx` belongs to an in-process subagent child registered
 * in `registry` by its session id.
 *
 * This is the only signal that identifies an **in-process** child (one sharing
 * the parent's `globalThis`); env-hint and filesystem heuristics identify
 * **process-based** subagents instead. The composition root uses this to decide
 * whether the instance owns the process-global service slot — a registered
 * child must not publish over its parent.
 */
export function isRegisteredSubagentChild(
  ctx: SubagentDetectionContext,
  registry: SubagentSessionRegistry,
): boolean {
  try {
    const sessionId = ctx.sessionManager.getSessionId();
    if (!sessionId) {
      return false;
    }
    return registry.has(sessionId);
  } catch {
    // getSessionId() unavailable — treat as not-a-registered-child.
    return false;
  }
}

export function isSubagentExecutionContext(
  ctx: SubagentDetectionContext,
  subagentSessionsDir: string,
  flavor: PathFlavor,
  registry?: SubagentSessionRegistry,
): boolean {
  // 1. Explicit registry — in-process subagent extensions register by child
  //    session id before bindExtensions(); checked first so it takes priority
  //    over heuristics. Each concurrent sibling has a unique session id, so
  //    one sibling's disposed event cannot affect another's registration.
  if (registry && isRegisteredSubagentChild(ctx, registry)) {
    return true;
  }

  const sessionDir = ctx.sessionManager.getSessionDir();

  // 2. Env vars — process-based subagent extensions (nicobailon/pi-subagents,
  //    HazAT/pi-interactive-subagents, pi-agent-router, etc.).
  for (const key of SUBAGENT_ENV_HINT_KEYS) {
    const value = process.env[key];
    if (typeof value === "string" && value.trim()) {
      return true;
    }
  }

  // 3. Filesystem path — fallback heuristic for extensions that store sessions
  //    under a known subagent root directory.
  if (!sessionDir) {
    return false;
  }

  const normalizedSessionDir = normalizeFilesystemPath(sessionDir, flavor);
  const normalizedSubagentRoot = normalizeFilesystemPath(
    subagentSessionsDir,
    flavor,
  );
  return flavor.isWithin(normalizedSessionDir, normalizedSubagentRoot);
}
