/**
 * Hooks token management via platform IPC.
 * Allows agent containers to enable/disable and rotate their hooks token.
 */
import path from "path";
import { getContext } from "../client.js";
import { writeIpcFile, clearStaleResult, pollJsonFile } from "../ipc.js";

function groupFolder(): string {
  return process.env.NANOCLAW_GROUP_FOLDER ?? "";
}

function tasksDir(ipcDir: string): string {
  return path.join(ipcDir, "tasks");
}

function resultFile(ipcDir: string): string {
  return path.join(ipcDir, "hooks-result.json");
}

export interface HooksConfig {
  ok: boolean;
  hooksToken: string | null;
  hooksEnabled: boolean;
}

async function sendAndWait(ipcDir: string, payload: object): Promise<HooksConfig> {
  const result = resultFile(ipcDir);
  clearStaleResult(result);
  writeIpcFile(tasksDir(ipcDir), { ...payload, groupFolder: groupFolder() });
  return pollJsonFile(result, (d) => (d as Record<string, unknown>).ok !== undefined, 15000) as Promise<HooksConfig>;
}

/**
 * Get the current hooks config for this group.
 */
export async function getHooksConfig(ipcDir?: string): Promise<HooksConfig> {
  const dir = ipcDir ?? getContext().ipcDir;
  return sendAndWait(dir, { type: "hooks_get" });
}

/**
 * Enable or disable hooks for this group.
 * Enabling auto-generates a token if none exists.
 */
export async function setHooksEnabled(enabled: boolean, ipcDir?: string): Promise<HooksConfig> {
  const dir = ipcDir ?? getContext().ipcDir;
  return sendAndWait(dir, { type: "hooks_set_enabled", enabled });
}

/**
 * Rotate the hooks bearer token, generating a new one.
 * Returns the new token.
 */
export async function rotateHooksToken(ipcDir?: string): Promise<HooksConfig> {
  const dir = ipcDir ?? getContext().ipcDir;
  return sendAndWait(dir, { type: "hooks_rotate" });
}

/** Result of an async hook call (agent trigger or wake) */
export interface HookAsyncResult {
  ok: boolean;
  queued?: boolean;
  error?: string;
}

/** Result of a synchronous hook invoke */
export interface HookInvokeResult {
  ok: boolean;
  result?: string | null;
  error?: string;
}

function hookResultFile(ipcDir: string, requestId: string): string {
  return path.join(ipcDir, `hooks-result-${requestId}.json`);
}

function genRequestId(): string {
  return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
}

/**
 * Trigger an async agent run via IPC.
 * Returns immediately after the task is queued.
 */
export async function triggerAgent(prompt: string, ipcDir?: string): Promise<HookAsyncResult> {
  const dir = ipcDir ?? getContext().ipcDir;
  const requestId = genRequestId();
  const resultFile = hookResultFile(dir, requestId);
  clearStaleResult(resultFile);
  writeIpcFile(path.join(dir, 'tasks'), { type: 'hooks_agent', prompt, requestId });
  return pollJsonFile(
    resultFile,
    (d) => typeof (d as Record<string, unknown>).ok === 'boolean',
    15_000,
  ) as Promise<HookAsyncResult>;
}

/**
 * Trigger a wake run via IPC (reads HEARTBEAT.md from the group workspace).
 * Returns immediately after the task is queued.
 */
export async function wake(ipcDir?: string): Promise<HookAsyncResult> {
  const dir = ipcDir ?? getContext().ipcDir;
  const requestId = genRequestId();
  const resultFile = hookResultFile(dir, requestId);
  clearStaleResult(resultFile);
  writeIpcFile(path.join(dir, 'tasks'), { type: 'hooks_wake', requestId });
  return pollJsonFile(
    resultFile,
    (d) => typeof (d as Record<string, unknown>).ok === 'boolean',
    15_000,
  ) as Promise<HookAsyncResult>;
}

/**
 * Invoke the agent synchronously via IPC and return the result.
 * Accepts either a free-form prompt or a tool name + args.
 * Waits up to 90 seconds for the agent to respond.
 */
export async function invoke(
  input: string | { tool: string; args?: Record<string, unknown> },
  ipcDir?: string,
): Promise<HookInvokeResult> {
  const dir = ipcDir ?? getContext().ipcDir;
  const requestId = genRequestId();
  const resultFile = hookResultFile(dir, requestId);
  clearStaleResult(resultFile);
  const payload =
    typeof input === 'string'
      ? { type: 'hooks_invoke', prompt: input, requestId }
      : { type: 'hooks_invoke', tool: input.tool, args: input.args ?? {}, requestId };
  writeIpcFile(path.join(dir, 'tasks'), payload);
  return pollJsonFile(
    resultFile,
    (d) => typeof (d as Record<string, unknown>).ok === 'boolean',
    90_000,
  ) as Promise<HookInvokeResult>;
}
