/**
 * Client for calling Control Plane API routes (which proxy to the dataplane via SDK)
 * This should be used in client components, not the SDK directly
 */

import { toast } from "sonner";

export interface WebhookHttpConfig {
  method: string;
  timeout_seconds: number;
  headers: Record<string, string>;
  params: Record<string, string>;
}

export interface Webhook {
  id: string;
  bank_id: string | null;
  url: string;
  event_types: string[];
  enabled: boolean;
  http_config: WebhookHttpConfig;
  created_at: string | null;
  updated_at: string | null;
}

export interface WebhookDelivery {
  id: string;
  webhook_id: string | null;
  url: string;
  event_type: string;
  status: string;
  attempts: number;
  next_retry_at: string | null;
  last_error: string | null;
  last_response_status: number | null;
  last_response_body: string | null;
  last_attempt_at: string | null;
  created_at: string | null;
  updated_at: string | null;
}

export interface MentalModel {
  id: string;
  bank_id: string;
  name: string;
  source_query: string;
  content: string;
  tags: string[];
  max_tokens: number;
  trigger: { refresh_after_consolidation: boolean };
  last_refreshed_at: string;
  created_at: string;
  reflect_response?: any;
}

export class ControlPlaneClient {
  private async fetchApi<T>(path: string, options?: RequestInit): Promise<T> {
    try {
      const response = await fetch(path, {
        ...options,
        headers: {
          "Content-Type": "application/json",
          ...options?.headers,
        },
      });

      if (!response.ok) {
        // Try to parse error response
        let errorMessage = `HTTP ${response.status}`;
        let errorDetails: string | undefined;

        try {
          const errorData = await response.json();
          errorMessage = errorData.error || errorMessage;
          errorDetails = errorData.details;
        } catch {
          // If JSON parse fails, try to get text
          try {
            const errorText = await response.text();
            if (errorText) {
              errorDetails = errorText;
            }
          } catch {
            // Ignore text parse errors
          }
        }

        // Show toast with different styles based on status code
        const description = errorDetails || errorMessage;
        const status = response.status;

        if (status >= 400 && status < 500) {
          // Client errors (4xx) - validation, bad request, etc. - show as warning
          toast.warning("Client Error", {
            description,
            duration: 5000,
          });
        } else if (status >= 500) {
          // Server errors (5xx) - show as error
          toast.error("Server Error", {
            description,
            duration: 5000,
          });
        } else {
          // Other HTTP errors - show as error
          toast.error("API Error", {
            description,
            duration: 5000,
          });
        }

        // Still throw error for callers that want to handle it
        const error = new Error(errorMessage);
        (error as any).status = response.status;
        (error as any).details = errorDetails;
        throw error;
      }

      return response.json();
    } catch (error) {
      // If it's not a response error (network error, etc.), show toast
      if (!(error as any).status) {
        toast.error("Network Error", {
          description: error instanceof Error ? error.message : "Failed to connect to server",
          duration: 5000,
        });
      }
      throw error;
    }
  }

  /**
   * List all banks
   */
  async listBanks() {
    return this.fetchApi<{ banks: any[] }>("/api/banks", { cache: "no-store" as RequestCache });
  }

  /**
   * Create a new bank
   */
  async createBank(bankId: string) {
    return this.fetchApi<{ bank_id: string }>("/api/banks", {
      method: "POST",
      body: JSON.stringify({ bank_id: bankId }),
    });
  }

  /**
   * Recall memories
   */
  async recall(params: {
    query: string;
    types?: string[];
    bank_id: string;
    budget?: string;
    max_tokens?: number;
    trace?: boolean;
    include?: {
      entities?: { max_tokens: number } | null;
      chunks?: { max_tokens: number } | null;
      source_facts?: { max_tokens?: number } | null;
    };
    query_timestamp?: string;
    tags?: string[];
    tags_match?: "any" | "all" | "any_strict" | "all_strict";
  }) {
    return this.fetchApi("/api/recall", {
      method: "POST",
      body: JSON.stringify(params),
    });
  }

  /**
   * Reflect and generate answer
   */
  async reflect(params: {
    query: string;
    bank_id: string;
    budget?: string;
    max_tokens?: number;
    include_facts?: boolean;
    include_tool_calls?: boolean;
    tags?: string[];
    tags_match?: "any" | "all" | "any_strict" | "all_strict";
  }) {
    return this.fetchApi("/api/reflect", {
      method: "POST",
      body: JSON.stringify(params),
    });
  }

  /**
   * Retain memories (batch)
   */
  async retain(params: {
    bank_id: string;
    items: Array<{
      content: string;
      timestamp?: string;
      context?: string;
      document_id?: string;
      metadata?: Record<string, string>;
      entities?: Array<{ text: string; type?: string }>;
      tags?: string[];
      observation_scopes?: "per_tag" | "combined" | "all_combinations" | string[][];
    }>;
    document_id?: string;
    async?: boolean;
  }) {
    const endpoint = params.async ? "/api/memories/retain_async" : "/api/memories/retain";
    return this.fetchApi<{ message?: string }>(endpoint, {
      method: "POST",
      body: JSON.stringify(params),
    });
  }

  /**
   * Get bank statistics
   */
  async getBankStats(bankId: string) {
    return this.fetchApi(`/api/stats/${bankId}`);
  }

  /**
   * Get graph data
   */
  async getGraph(params: {
    bank_id: string;
    type?: string;
    limit?: number;
    q?: string;
    tags?: string[];
  }) {
    const queryParams = new URLSearchParams();
    queryParams.append("bank_id", params.bank_id);
    if (params.type) queryParams.append("type", params.type);
    if (params.limit) queryParams.append("limit", params.limit.toString());
    if (params.q) queryParams.append("q", params.q);
    if (params.tags && params.tags.length > 0) {
      params.tags.forEach((tag) => queryParams.append("tags", tag));
    }
    return this.fetchApi(`/api/graph?${queryParams}`);
  }

  /**
   * List operations with optional filtering and pagination
   */
  async listOperations(
    bankId: string,
    options?: { status?: string; type?: string; limit?: number; offset?: number }
  ) {
    const params = new URLSearchParams();
    if (options?.status) params.append("status", options.status);
    if (options?.type) params.append("type", options.type);
    if (options?.limit) params.append("limit", options.limit.toString());
    if (options?.offset) params.append("offset", options.offset.toString());
    const query = params.toString();
    return this.fetchApi<{
      bank_id: string;
      total: number;
      limit: number;
      offset: number;
      operations: Array<{
        id: string;
        task_type: string;
        items_count: number;
        document_id: string | null;
        created_at: string;
        status: string;
        error_message: string | null;
      }>;
    }>(`/api/operations/${bankId}${query ? `?${query}` : ""}`);
  }

  /**
   * Cancel a pending operation
   */
  async cancelOperation(bankId: string, operationId: string) {
    return this.fetchApi<{
      success: boolean;
      message: string;
      operation_id: string;
    }>(`/api/operations/${bankId}?operation_id=${operationId}`, {
      method: "DELETE",
    });
  }

  /**
   * Retry a failed operation
   */
  async retryOperation(bankId: string, operationId: string) {
    return this.fetchApi<{
      success: boolean;
      message: string;
      operation_id: string;
    }>(`/api/banks/${bankId}/operations/${operationId}`, {
      method: "POST",
    });
  }

  /**
   * List entities
   */
  async listEntities(params: { bank_id: string; limit?: number; offset?: number }) {
    const queryParams = new URLSearchParams();
    queryParams.append("bank_id", params.bank_id);
    if (params.limit) queryParams.append("limit", params.limit.toString());
    if (params.offset) queryParams.append("offset", params.offset.toString());
    return this.fetchApi<{
      items: any[];
      total: number;
      limit: number;
      offset: number;
    }>(`/api/entities?${queryParams}`);
  }

  /**
   * Get entity details
   */
  async getEntity(entityId: string, bankId: string) {
    return this.fetchApi(`/api/entities/${entityId}?bank_id=${bankId}`);
  }

  /**
   * Regenerate entity observations
   */
  async regenerateEntityObservations(entityId: string, bankId: string) {
    return this.fetchApi(`/api/entities/${entityId}/regenerate?bank_id=${bankId}`, {
      method: "POST",
    });
  }

  /**
   * List documents
   */
  async listDocuments(params: { bank_id: string; q?: string; limit?: number; offset?: number }) {
    const queryParams = new URLSearchParams();
    queryParams.append("bank_id", params.bank_id);
    if (params.q) queryParams.append("q", params.q);
    if (params.limit) queryParams.append("limit", params.limit.toString());
    if (params.offset) queryParams.append("offset", params.offset.toString());
    return this.fetchApi(`/api/documents?${queryParams}`);
  }

  /**
   * Get document
   */
  async getDocument(documentId: string, bankId: string) {
    return this.fetchApi(`/api/documents/${documentId}?bank_id=${bankId}`);
  }

  /**
   * Update tags on a document and its associated memory units
   */
  async updateDocument(documentId: string, bankId: string, tags: string[]) {
    return this.fetchApi<{ success: boolean }>(
      `/api/documents/${encodeURIComponent(documentId)}?bank_id=${bankId}`,
      {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ tags }),
      }
    );
  }

  /**
   * Delete document and all its associated memory units
   */
  async deleteDocument(documentId: string, bankId: string) {
    return this.fetchApi<{
      success: boolean;
      message: string;
      document_id: string;
      memory_units_deleted: number;
    }>(`/api/documents/${documentId}?bank_id=${bankId}`, {
      method: "DELETE",
    });
  }

  /**
   * Delete an entire memory bank and all its data
   */
  async deleteBank(bankId: string) {
    return this.fetchApi<{
      success: boolean;
      message: string;
      deleted_count: number;
    }>(`/api/banks/${bankId}`, {
      method: "DELETE",
    });
  }

  /**
   * Clear all observations for a bank
   */
  async clearObservations(bankId: string) {
    return this.fetchApi<{
      success: boolean;
      message: string;
      deleted_count: number;
    }>(`/api/banks/${bankId}/observations`, {
      method: "DELETE",
    });
  }

  /**
   * Trigger consolidation for a bank
   */
  async triggerConsolidation(bankId: string) {
    return this.fetchApi<{
      operation_id: string;
      deduplicated: boolean;
    }>(`/api/banks/${bankId}/consolidate`, {
      method: "POST",
    });
  }

  /**
   * Get chunk
   */
  async getChunk(chunkId: string) {
    return this.fetchApi(`/api/chunks/${chunkId}`);
  }

  /**
   * Get a single memory by ID
   */
  async getMemory(memoryId: string, bankId: string) {
    return this.fetchApi<{
      id: string;
      text: string;
      context: string;
      date: string;
      type: string;
      mentioned_at: string | null;
      occurred_start: string | null;
      occurred_end: string | null;
      entities: string[];
      document_id: string | null;
      chunk_id: string | null;
      tags: string[];
      observation_scopes: string | string[][] | null;
      history?: {
        previous_text: string;
        previous_tags: string[];
        previous_occurred_start: string | null;
        previous_occurred_end: string | null;
        previous_mentioned_at: string | null;
        changed_at: string;
        new_source_memory_ids: string[];
      }[];
    }>(`/api/memories/${memoryId}?bank_id=${bankId}`);
  }

  /**
   * Get the history of an observation with resolved source facts
   */
  async getObservationHistory(memoryId: string, bankId: string) {
    return this.fetchApi<
      {
        previous_text: string;
        previous_tags: string[];
        previous_occurred_start: string | null;
        previous_occurred_end: string | null;
        previous_mentioned_at: string | null;
        changed_at: string;
        new_source_memory_ids: string[];
        source_facts: {
          id: string;
          text: string | null;
          type: string | null;
          context: string | null;
          is_new: boolean;
        }[];
      }[]
    >(`/api/memories/${memoryId}/history?bank_id=${bankId}`);
  }

  /**
   * Get bank profile
   */
  async getBankProfile(bankId: string) {
    return this.fetchApi<{
      bank_id: string;
      name: string;
      disposition: {
        skepticism: number;
        literalism: number;
        empathy: number;
      };
      mission: string;
      background?: string; // Deprecated, kept for backwards compatibility
    }>(`/api/profile/${bankId}`);
  }

  /**
   * Set bank mission
   */
  async setBankMission(bankId: string, mission: string) {
    return this.fetchApi(`/api/banks/${bankId}`, {
      method: "PATCH",
      body: JSON.stringify({ mission }),
    });
  }

  /**
   * List directives for a bank
   */
  async listDirectives(bankId: string, tags?: string[], tagsMatch?: string) {
    const params = new URLSearchParams();
    if (tags && tags.length > 0) {
      tags.forEach((t) => params.append("tags", t));
    }
    if (tagsMatch) {
      params.append("tags_match", tagsMatch);
    }
    const query = params.toString();
    return this.fetchApi<{
      items: Array<{
        id: string;
        bank_id: string;
        name: string;
        content: string;
        priority: number;
        is_active: boolean;
        tags: string[];
        created_at: string;
        updated_at: string;
      }>;
    }>(`/api/banks/${bankId}/directives${query ? `?${query}` : ""}`);
  }

  /**
   * Create a directive
   */
  async createDirective(
    bankId: string,
    params: {
      name: string;
      content: string;
      priority?: number;
      is_active?: boolean;
      tags?: string[];
    }
  ) {
    return this.fetchApi<{
      id: string;
      bank_id: string;
      name: string;
      content: string;
      priority: number;
      is_active: boolean;
      tags: string[];
      created_at: string;
      updated_at: string;
    }>(`/api/banks/${bankId}/directives`, {
      method: "POST",
      body: JSON.stringify(params),
    });
  }

  /**
   * Get a directive
   */
  async getDirective(bankId: string, directiveId: string) {
    return this.fetchApi<{
      id: string;
      bank_id: string;
      name: string;
      content: string;
      priority: number;
      is_active: boolean;
      tags: string[];
      created_at: string;
      updated_at: string;
    }>(`/api/banks/${bankId}/directives/${directiveId}`);
  }

  /**
   * Delete a directive
   */
  async deleteDirective(bankId: string, directiveId: string) {
    return this.fetchApi(`/api/banks/${bankId}/directives/${directiveId}`, {
      method: "DELETE",
    });
  }

  /**
   * Update a directive
   */
  async updateDirective(
    bankId: string,
    directiveId: string,
    params: {
      name?: string;
      content?: string;
      priority?: number;
      is_active?: boolean;
      tags?: string[];
    }
  ) {
    return this.fetchApi<{
      id: string;
      bank_id: string;
      name: string;
      content: string;
      priority: number;
      is_active: boolean;
      tags: string[];
      created_at: string;
      updated_at: string;
    }>(`/api/banks/${bankId}/directives/${directiveId}`, {
      method: "PATCH",
      body: JSON.stringify(params),
    });
  }

  /**
   * Get operation status
   */
  async getOperationStatus(bankId: string, operationId: string) {
    return this.fetchApi<{
      operation_id: string;
      status: "pending" | "completed" | "failed" | "not_found";
      operation_type: string | null;
      created_at: string | null;
      updated_at: string | null;
      completed_at: string | null;
      error_message: string | null;
    }>(`/api/banks/${bankId}/operations/${operationId}`);
  }

  /**
   * Update bank profile
   */
  async updateBankProfile(
    bankId: string,
    profile: {
      name?: string;
      disposition?: {
        skepticism: number;
        literalism: number;
        empathy: number;
      };
      mission?: string;
    }
  ) {
    return this.fetchApi(`/api/profile/${bankId}`, {
      method: "PUT",
      body: JSON.stringify(profile),
    });
  }

  // ============= OBSERVATIONS (auto-consolidated, read-only) =============

  /**
   * List observations for a bank (auto-consolidated knowledge)
   */
  async listObservations(bankId: string, tags?: string[], tagsMatch?: string) {
    const params = new URLSearchParams();
    if (tags && tags.length > 0) {
      tags.forEach((t) => params.append("tags", t));
    }
    if (tagsMatch) {
      params.append("tags_match", tagsMatch);
    }
    const query = params.toString();
    return this.fetchApi<{
      items: Array<{
        id: string;
        bank_id: string;
        text: string;
        proof_count: number;
        history: Array<{
          previous_text: string;
          changed_at: string;
          reason: string;
        }>;
        tags: string[];
        source_memory_ids: string[];
        source_memories: Array<{
          id: string;
          text: string;
          type: string;
          context?: string;
          occurred_start?: string;
          mentioned_at?: string;
        }>;
        created_at: string;
        updated_at: string;
      }>;
    }>(`/api/banks/${bankId}/observations${query ? `?${query}` : ""}`);
  }

  /**
   * Get an observation with source memories
   */
  async getObservation(bankId: string, observationId: string) {
    return this.fetchApi<{
      id: string;
      bank_id: string;
      text: string;
      proof_count: number;
      history: Array<{
        previous_text: string;
        changed_at: string;
        reason: string;
      }>;
      tags: string[];
      source_memory_ids: string[];
      source_memories: Array<{
        id: string;
        text: string;
        type: string;
        context?: string;
        occurred_start?: string;
        mentioned_at?: string;
      }>;
      created_at: string;
      updated_at: string;
    }>(`/api/banks/${bankId}/observations/${observationId}`);
  }

  // ============= MENTAL MODELS (stored reflect responses) =============

  /**
   * List mental models for a bank
   */
  async listMentalModels(bankId: string, tags?: string[], tagsMatch?: string) {
    const params = new URLSearchParams();
    if (tags && tags.length > 0) {
      tags.forEach((t) => params.append("tags", t));
    }
    if (tagsMatch) {
      params.append("tags_match", tagsMatch);
    }
    const query = params.toString();
    return this.fetchApi<{
      items: Array<{
        id: string;
        bank_id: string;
        name: string;
        source_query: string;
        content: string;
        tags: string[];
        max_tokens: number;
        trigger: { refresh_after_consolidation: boolean };
        last_refreshed_at: string;
        created_at: string;
        reflect_response?: {
          text: string;
          based_on: Record<string, Array<{ id: string; text: string; type: string }>>;
        };
      }>;
    }>(`/api/banks/${bankId}/mental-models${query ? `?${query}` : ""}`);
  }

  /**
   * Create a mental model (async - content auto-generated in background)
   * Returns operation_id to track progress
   */
  async createMentalModel(
    bankId: string,
    params: {
      id?: string;
      name: string;
      source_query: string;
      tags?: string[];
      max_tokens?: number;
      trigger?: { refresh_after_consolidation: boolean };
    }
  ) {
    return this.fetchApi<{
      operation_id: string;
    }>(`/api/banks/${bankId}/mental-models`, {
      method: "POST",
      body: JSON.stringify(params),
    });
  }

  /**
   * Get a mental model
   */
  async getMentalModel(bankId: string, mentalModelId: string): Promise<MentalModel> {
    return this.fetchApi<MentalModel>(`/api/banks/${bankId}/mental-models/${mentalModelId}`);
  }

  /**
   * Update a mental model
   */
  async updateMentalModel(
    bankId: string,
    mentalModelId: string,
    params: {
      name?: string;
      source_query?: string;
      max_tokens?: number;
      tags?: string[];
      trigger?: { refresh_after_consolidation: boolean };
    }
  ) {
    return this.fetchApi<{
      id: string;
      bank_id: string;
      name: string;
      source_query: string;
      content: string;
      tags: string[];
      max_tokens: number;
      trigger: { refresh_after_consolidation: boolean };
      last_refreshed_at: string;
      created_at: string;
      reflect_response?: {
        text: string;
        based_on: Record<string, Array<{ id: string; text: string; type: string }>>;
      };
    }>(`/api/banks/${bankId}/mental-models/${mentalModelId}`, {
      method: "PATCH",
      body: JSON.stringify(params),
    });
  }

  /**
   * Delete a mental model
   */
  async deleteMentalModel(bankId: string, mentalModelId: string) {
    return this.fetchApi(`/api/banks/${bankId}/mental-models/${mentalModelId}`, {
      method: "DELETE",
    });
  }

  /**
   * Refresh a mental model (re-run source query) - async operation
   */
  async refreshMentalModel(bankId: string, mentalModelId: string) {
    return this.fetchApi<{
      operation_id: string;
    }>(`/api/banks/${bankId}/mental-models/${mentalModelId}/refresh`, {
      method: "POST",
    });
  }

  /**
   * Get the refresh history of a mental model
   */
  async getMentalModelHistory(bankId: string, mentalModelId: string) {
    return this.fetchApi<
      {
        previous_content: string | null;
        changed_at: string;
      }[]
    >(`/api/banks/${bankId}/mental-models/${mentalModelId}/history`);
  }

  /**
   * Get API version and feature flags
   * Use this to check which capabilities are available in the dataplane
   */
  async getVersion() {
    return this.fetchApi<{
      api_version: string;
      features: {
        observations: boolean;
        mcp: boolean;
        worker: boolean;
        bank_config_api: boolean;
        file_upload_api: boolean;
      };
    }>("/api/version");
  }

  /**
   * Upload files for retain (uses file conversion API)
   * Requires file_upload_api feature flag to be enabled
   * Converter is configured server-side via HINDSIGHT_API_FILE_CONVERTER
   */
  async uploadFiles(params: {
    bank_id: string;
    files: File[];
    document_tags?: string[];
    async?: boolean;
    files_metadata?: Array<{
      document_id?: string;
      context?: string;
      metadata?: Record<string, any>;
      tags?: string[];
      timestamp?: string;
    }>;
  }) {
    const formData = new FormData();

    // Add files
    params.files.forEach((file) => {
      formData.append("files", file);
    });

    // Add request JSON (including bank_id)
    const requestData: any = {
      bank_id: params.bank_id,
      async: params.async ?? true,
    };
    if (params.document_tags) requestData.document_tags = params.document_tags;
    if (params.files_metadata) requestData.files_metadata = params.files_metadata;

    formData.append("request", JSON.stringify(requestData));

    // Use fetch directly for multipart/form-data
    const response = await fetch(`/api/files/retain`, {
      method: "POST",
      body: formData,
      // Don't set Content-Type - browser will set it with boundary
    });

    if (!response.ok) {
      let errorMessage = `HTTP ${response.status}`;
      try {
        const errorData = await response.json();
        errorMessage = errorData.error || errorMessage;
      } catch {
        // Ignore parse errors
      }
      const error = new Error(errorMessage);
      (error as any).status = response.status;
      throw error;
    }

    return response.json();
  }

  /**
   * Get bank configuration (resolved with hierarchy)
   */
  async getBankConfig(bankId: string) {
    return this.fetchApi<{
      bank_id: string;
      config: Record<string, any>;
      overrides: Record<string, any>;
    }>(`/api/banks/${bankId}/config`);
  }

  /**
   * Update bank configuration overrides
   */
  async updateBankConfig(bankId: string, updates: Record<string, any>) {
    return this.fetchApi<{
      bank_id: string;
      config: Record<string, any>;
      overrides: Record<string, any>;
    }>(`/api/banks/${bankId}/config`, {
      method: "PATCH",
      body: JSON.stringify({ updates }),
    });
  }

  /**
   * Reset bank configuration to defaults
   */
  async resetBankConfig(bankId: string) {
    return this.fetchApi<{
      bank_id: string;
      config: Record<string, any>;
      overrides: Record<string, any>;
    }>(`/api/banks/${bankId}/config`, {
      method: "DELETE",
    });
  }

  /**
   * List webhooks for a bank
   */
  async listWebhooks(bankId: string): Promise<{ items: Webhook[] }> {
    return this.fetchApi<{ items: Webhook[] }>(`/api/banks/${bankId}/webhooks`);
  }

  /**
   * Create a webhook
   */
  async createWebhook(
    bankId: string,
    params: {
      url: string;
      secret?: string;
      event_types?: string[];
      enabled?: boolean;
      http_config?: WebhookHttpConfig;
    }
  ): Promise<Webhook> {
    return this.fetchApi<Webhook>(`/api/banks/${bankId}/webhooks`, {
      method: "POST",
      body: JSON.stringify(params),
    });
  }

  /**
   * Update a webhook (PATCH — only provided fields are changed)
   */
  async updateWebhook(
    bankId: string,
    webhookId: string,
    params: {
      url?: string;
      secret?: string | null;
      event_types?: string[];
      enabled?: boolean;
      http_config?: WebhookHttpConfig;
    }
  ): Promise<Webhook> {
    return this.fetchApi<Webhook>(`/api/banks/${bankId}/webhooks/${webhookId}`, {
      method: "PATCH",
      body: JSON.stringify(params),
    });
  }

  /**
   * Delete a webhook
   */
  async deleteWebhook(bankId: string, webhookId: string): Promise<{ success: boolean }> {
    return this.fetchApi<{ success: boolean }>(`/api/banks/${bankId}/webhooks/${webhookId}`, {
      method: "DELETE",
    });
  }

  /**
   * List webhook deliveries
   */
  async listWebhookDeliveries(
    bankId: string,
    webhookId: string,
    limit?: number,
    cursor?: string
  ): Promise<{ items: WebhookDelivery[]; next_cursor: string | null }> {
    const params = new URLSearchParams();
    if (limit) params.append("limit", limit.toString());
    if (cursor) params.append("cursor", cursor);
    const query = params.toString();
    return this.fetchApi<{ items: WebhookDelivery[]; next_cursor: string | null }>(
      `/api/banks/${bankId}/webhooks/${webhookId}/deliveries${query ? `?${query}` : ""}`
    );
  }
}

// Export singleton instance
export const client = new ControlPlaneClient();
