// ============================================================================
// H33 Agent SDK — Tool attestation helpers (MCP-native)
// ============================================================================

import { createHash } from 'crypto';
import type { H33Session } from './session.js';
import type { AgentReceipt, ToolCallStatus } from './types.js';

/**
 * Hash a tool request payload for attestation. Produces a SHA-256 hex
 * digest of the canonicalized JSON. Never send raw content to the API --
 * always hash first.
 *
 * @param request - The tool request payload (any serializable value)
 * @returns SHA-256 hex string
 */
export function hashToolRequest(request: unknown): string {
  const canonical = JSON.stringify(request, Object.keys(request as object).sort());
  return createHash('sha256').update(canonical).digest('hex');
}

/**
 * Hash a tool response payload for attestation. Produces a SHA-256 hex
 * digest of the canonicalized JSON.
 *
 * @param response - The tool response payload (any serializable value)
 * @returns SHA-256 hex string
 */
export function hashToolResponse(response: unknown): string {
  const canonical = JSON.stringify(response, Object.keys(response as object).sort());
  return createHash('sha256').update(canonical).digest('hex');
}

/**
 * Create a canonical tool name following H33 naming conventions.
 *
 * @param provider   - Tool provider (e.g. 'anthropic', 'acme')
 * @param capability - Tool capability (e.g. 'claude', 'database')
 * @param version    - Version string (e.g. 'v1')
 * @returns Canonical name like 'h33.tool.anthropic.claude.v1'
 *
 * @example
 * ```ts
 * toolName('anthropic', 'claude', 'v1')
 * // => 'h33.tool.anthropic.claude.v1'
 * ```
 */
export function toolName(provider: string, capability: string, version: string): string {
  return `h33.tool.${provider}.${capability}.${version}`;
}

/**
 * Wrap any tool handler with automatic H33 attestation. Every invocation
 * of the wrapped function will:
 *
 * 1. Hash the input
 * 2. Execute the original handler
 * 3. Hash the output
 * 4. Attest the tool call via the H33 API
 * 5. Return both the result and the receipt
 *
 * This is the primary integration point for MCP servers. Wrap each tool
 * handler and every call is cryptographically attested with zero code
 * changes to the handler itself.
 *
 * @param session     - Active H33 session
 * @param name        - Canonical tool name (use `toolName()` helper)
 * @param handler     - The original tool handler function
 * @returns Wrapped handler that returns `{ result, receipt }`
 *
 * @example
 * ```ts
 * const attested = attestedTool(session, 'h33.tool.acme.search.v1', async (query: string) => {
 *   return await database.search(query);
 * });
 *
 * const { result, receipt } = await attested('open claims');
 * console.log(result);                       // search results
 * console.log(receipt.verification_url);      // public proof URL
 * ```
 */
export function attestedTool<T, R>(
  session: H33Session,
  name: string,
  handler: (input: T) => Promise<R>
): (input: T) => Promise<{ result: R; receipt: AgentReceipt }> {
  return async (input: T): Promise<{ result: R; receipt: AgentReceipt }> => {
    const requestHash = hashToolRequest(input);
    const start = Date.now();

    let result: R;
    let status: ToolCallStatus;
    let responseHash: string | undefined;

    try {
      result = await handler(input);
      status = 'success';
      responseHash = hashToolResponse(result);
    } catch (err) {
      status = 'failure';
      // Attest the failure, then re-throw
      await session.attestTool(name, requestHash, {
        status,
        latency_ms: Date.now() - start,
      });
      throw err;
    }

    const latencyMs = Date.now() - start;

    const receipt = await session.attestTool(name, requestHash, {
      response_hash: responseHash,
      status,
      latency_ms: latencyMs,
    });

    return { result, receipt };
  };
}
