// ============================================================================
// H33 Agent SDK — Session management wrapper
// ============================================================================

import type { H33AgentClient } from './client.js';
import type {
  AgentReceipt,
  AttestActionRequest,
  AttestToolRequest,
  RedactionLevel,
  ToolCallStatus,
} from './types.js';

/**
 * Convenience wrapper around a live H33 session. Auto-fills the session ID
 * and agent ID on every call, tracks action count, and provides a clean
 * fluent API.
 *
 * @example
 * ```ts
 * const receipt = await h33.startSession({ agent_id: agentId, duration_secs: 3600 });
 * const session = new H33Session(h33, receipt);
 *
 * await session.attestAction('api_call', 'Called weather API', inputHash);
 * await session.attestTool('h33.tool.weather.v1', requestHash);
 * await session.end();
 * ```
 */
export class H33Session {
  private readonly client: H33AgentClient;
  private readonly _sessionId: string;
  private readonly _agentId: string;
  private _actionCount: number = 0;
  private _ended: boolean = false;

  /**
   * Create a session wrapper from a start-session receipt.
   *
   * @param client  - The H33AgentClient instance
   * @param receipt - The AgentReceipt returned by `startSession()`
   */
  constructor(client: H33AgentClient, receipt: AgentReceipt) {
    this.client = client;
    this._sessionId = receipt.session_id ?? receipt.node_id;
    this._agentId = receipt.agent_id;
  }

  /** The session ID */
  get id(): string {
    return this._sessionId;
  }

  /** The agent ID that owns this session */
  get agentId(): string {
    return this._agentId;
  }

  /** Number of attested actions so far */
  get actions(): number {
    return this._actionCount;
  }

  /** Whether this session has been ended */
  get ended(): boolean {
    return this._ended;
  }

  /**
   * Attest an action within this session.
   *
   * @param actionType  - Label for the action (e.g. 'database_query')
   * @param summary     - Human-readable description
   * @param inputHash   - SHA-256 of the action input
   * @param opts        - Optional overrides (output_hash, redaction_level, metadata)
   */
  async attestAction(
    actionType: string,
    summary: string,
    inputHash: string,
    opts?: Partial<Omit<AttestActionRequest, 'session_id' | 'action_type' | 'action_summary' | 'input_hash'>>
  ): Promise<AgentReceipt> {
    this.assertOpen();
    const receipt = await this.client.attestAction({
      session_id: this._sessionId,
      action_type: actionType,
      action_summary: summary,
      input_hash: inputHash,
      ...opts,
    });
    this._actionCount++;
    return receipt;
  }

  /**
   * Attest a tool call within this session.
   *
   * @param toolName    - Canonical tool name
   * @param requestHash - SHA-256 of the tool request payload
   * @param opts        - Optional overrides (response_hash, status, latency_ms, metadata)
   */
  async attestTool(
    toolName: string,
    requestHash: string,
    opts?: Partial<Omit<AttestToolRequest, 'session_id' | 'tool_name' | 'request_hash'>>
  ): Promise<AgentReceipt> {
    this.assertOpen();
    const receipt = await this.client.attestTool({
      session_id: this._sessionId,
      tool_name: toolName,
      request_hash: requestHash,
      ...opts,
    });
    this._actionCount++;
    return receipt;
  }

  /**
   * Checkpoint the current memory / context state.
   *
   * @param memoryHash  - SHA-256 of the full memory state
   * @param contextHash - SHA-256 of the current context window
   * @param sizeBytes   - Size of the memory state in bytes
   */
  async checkpointMemory(
    memoryHash: string,
    contextHash: string,
    sizeBytes: number
  ): Promise<AgentReceipt> {
    this.assertOpen();
    const receipt = await this.client.checkpointMemory({
      session_id: this._sessionId,
      memory_hash: memoryHash,
      context_hash: contextHash,
      size_bytes: sizeBytes,
    });
    this._actionCount++;
    return receipt;
  }

  /**
   * Evaluate a policy before performing an action.
   *
   * @param actionType - Action type to evaluate
   * @param target     - Target resource or entity
   * @param params     - Additional parameters for policy evaluation
   */
  async evaluatePolicy(
    actionType: string,
    target: string,
    params?: Record<string, unknown>
  ): Promise<AgentReceipt> {
    this.assertOpen();
    return this.client.evaluatePolicy({
      session_id: this._sessionId,
      action_type: actionType,
      target,
      params,
    });
  }

  /**
   * Check for data exposure before accessing a classified resource.
   *
   * @param dataClass  - Data classification label (e.g. 'PII', 'PHI')
   * @param operation  - Operation being attempted (e.g. 'read', 'export')
   */
  async checkExposure(
    dataClass: string,
    operation: string
  ): Promise<AgentReceipt> {
    this.assertOpen();
    return this.client.checkExposure({
      session_id: this._sessionId,
      data_class: dataClass,
      operation,
    });
  }

  /**
   * End this session. Seals the chain and produces a final receipt.
   * The session cannot be used after this call.
   *
   * @param summary - Optional closing summary
   */
  async end(summary?: string): Promise<AgentReceipt> {
    this.assertOpen();
    const receipt = await this.client.endSession(this._sessionId, summary);
    this._ended = true;
    return receipt;
  }

  /** Throws if the session has already been ended */
  private assertOpen(): void {
    if (this._ended) {
      throw new Error(`H33 session ${this._sessionId} has already been ended`);
    }
  }
}
