import type {
  SendRequest,
  SendResponse,
  ApplicationConfig,
  RoutingConfig,
  SSEEvent,
  LogRecord,
} from '@zyphon/shared-types';

export interface AdminStats {
  sent_today: number;
  success_rate: number | null;
  failed_today: number;
  active_apps: number;
}

export interface AdminMessageAttempt {
  p: string;
  ok: boolean;
  e?: string;
}

export interface AdminMessageAttachment {
  filename: string;
  contentType: string;
  size_bytes: number;
  url?: string;
}

export interface AdminMessage {
  id: string;
  request_id?: string;
  ts: number;
  app: string;
  channel: 'email' | 'sms' | 'whatsapp';
  to: string;
  subject?: string;
  body?: string;
  status: 'sending' | 'sent' | 'delivered' | 'failed' | 'blocked';
  via: string | null;
  /** Provider delivery attempts (not file attachments). */
  att: AdminMessageAttempt[];
  /** File attachment metadata only — bytes are never stored or returned. */
  attachments?: AdminMessageAttachment[];
  ms: number;
  note: string;
}

export interface AdminProvider {
  id: string;
  channel: 'email' | 'sms' | 'whatsapp';
  type: string;
  name: string;
  config: Record<string, any>;
  /** True when encrypted credentials exist; never includes secret values. */
  secret_set?: boolean;
  status: 'Connected' | 'Paused briefly' | 'Needs attention';
  sent: number;
  failed: number;
  cf: number;
  until?: number;
  circuit?: { circuit_state: string };
  /** Applications that explicitly bind this provider. */
  used_by?: string[];
  /** Last provider/circuit error, when status is not Connected. */
  last_error?: string;
}

export interface AdminSettings {
  retry: {
    attempts: number;
    wait: number;
  };
  circuit_breaker: {
    trip: number;
    pause: number;
  };
  retention_days: number;
}

export interface ZyphonClientOptions {
  baseUrl: string;
  apiKey: string;
  timeoutMs?: number;
  fetchFn?: typeof fetch;
}

export interface SendOptions {
  sync?: boolean;
  idempotencyKey?: string;
}

export class ZyphonError extends Error {
  public readonly code: string;
  public readonly status: number;
  public readonly details?: unknown;

  constructor(message: string, code: string, status: number, details?: unknown) {
    super(message);
    this.name = 'ZyphonError';
    this.code = code;
    this.status = status;
    this.details = details;
  }
}

export class ZyphonClient {
  private readonly baseUrl: string;
  private readonly apiKey: string;
  private readonly timeoutMs: number;
  private readonly fetchFn: typeof fetch;

  constructor(options: ZyphonClientOptions) {
    this.baseUrl = options.baseUrl.replace(/\/$/, '');
    this.apiKey = options.apiKey;
    this.timeoutMs = options.timeoutMs ?? 10000;
    this.fetchFn = options.fetchFn ?? globalThis.fetch.bind(globalThis);
  }

  public async send(request: SendRequest, options: SendOptions = {}): Promise<SendResponse> {
    const url = new URL(`${this.baseUrl}/api/v1/messages`);
    if (options.sync !== undefined) {
      url.searchParams.set('sync', String(options.sync));
    }

    const headers: Record<string, string> = {
      'Content-Type': 'application/json',
      'X-API-Key': this.apiKey,
    };

    if (options.idempotencyKey) {
      headers['X-Idempotency-Key'] = options.idempotencyKey;
    }

    const res = await this.request(url.toString(), {
      method: 'POST',
      headers,
      body: JSON.stringify(request),
    });

    return (await res.json()) as SendResponse;
  }

  public async getMessage(messageId: string): Promise<SendResponse> {
    const res = await this.request(`${this.baseUrl}/api/v1/messages/${encodeURIComponent(messageId)}`, {
      method: 'GET',
      headers: {
        'X-API-Key': this.apiKey,
      },
    });

    return (await res.json()) as SendResponse;
  }

  public async health(): Promise<{ status: string; uptime: number; timestamp: number }> {
    const res = await this.request(`${this.baseUrl}/api/v1/health`, {
      method: 'GET',
    });

    return (await res.json()) as { status: string; uptime: number; timestamp: number };
  }

  private async request(url: string, init: RequestInit): Promise<Response> {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), this.timeoutMs);

    try {
      const response = await this.fetchFn(url, {
        ...init,
        signal: controller.signal,
      });

      if (!response.ok) {
        let errorData: any = {};
        try {
          errorData = await response.json();
        } catch {
          errorData = { message: response.statusText };
        }

        const code = errorData?.error?.code || errorData?.code || 'REQUEST_FAILED';
        const msg = errorData?.error?.message || errorData?.message || response.statusText;
        throw new ZyphonError(msg, code, response.status, errorData);
      }

      return response;
    } catch (err: any) {
      if (err.name === 'AbortError') {
        throw new ZyphonError(`Request timed out after ${this.timeoutMs}ms`, 'TIMEOUT', 504);
      }
      if (err instanceof ZyphonError) {
        throw err;
      }
      throw new ZyphonError(err.message || 'Network error', 'INTERNAL_ERROR', 500, err);
    } finally {
      clearTimeout(timeout);
    }
  }
}

export interface AdminClientOptions {
  baseUrl: string;
  adminToken?: string;
  timeoutMs?: number;
  fetchFn?: typeof fetch;
}

export class ZyphonAdminClient {
  private readonly baseUrl: string;
  private adminToken?: string;
  private csrfToken?: string;
  private readonly timeoutMs: number;
  private readonly fetchFn: typeof fetch;

  constructor(options: AdminClientOptions) {
    this.baseUrl = options.baseUrl.replace(/\/$/, '');
    this.adminToken = options.adminToken;
    this.timeoutMs = options.timeoutMs ?? 15000;
    this.fetchFn = options.fetchFn ?? globalThis.fetch.bind(globalThis);
  }

  public setToken(token: string): void {
    this.adminToken = token;
  }

  public setCsrfToken(token: string): void {
    this.csrfToken = token;
  }

  public async login(password: string): Promise<{ success: boolean; token: string; csrfToken?: string; role?: string }> {
    const res = await this.request(`${this.baseUrl}/admin/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ password }),
      skipCsrf: true,
    } as RequestInit & { skipCsrf?: boolean });

    const data = (await res.json()) as {
      success: boolean;
      token: string;
      csrfToken?: string;
      csrf_token?: string;
      role?: string;
    };
    if (data.token) {
      this.adminToken = data.token;
    }
    const csrf = data.csrfToken || data.csrf_token;
    if (csrf) {
      this.csrfToken = csrf;
    }
    return { success: data.success, token: data.token, csrfToken: csrf, role: data.role };
  }

  public async me(): Promise<{ authenticated: boolean; role?: string }> {
    try {
      const res = await this.request(`${this.baseUrl}/admin/auth/me`);
      return (await res.json()) as { authenticated: boolean; role?: string };
    } catch (err: unknown) {
      if (err instanceof ZyphonError && err.status === 401) {
        return { authenticated: false };
      }
      throw err;
    }
  }

  public async logout(): Promise<{ success: boolean }> {
    const res = await this.request(`${this.baseUrl}/admin/auth/logout`, {
      method: 'POST',
      skipCsrf: true,
    } as RequestInit & { skipCsrf?: boolean });
    this.adminToken = undefined;
    this.csrfToken = undefined;
    return (await res.json()) as { success: boolean };
  }

  public async getCatalog(): Promise<Record<string, unknown[]>> {
    const res = await this.request(`${this.baseUrl}/admin/catalog`);
    return (await res.json()) as Record<string, unknown[]>;
  }

  public async getTemplates(): Promise<{ templates: Array<Record<string, unknown>> }> {
    const res = await this.request(`${this.baseUrl}/admin/templates`);
    return (await res.json()) as { templates: Array<Record<string, unknown>> };
  }

  public async createTemplate(template: Record<string, unknown>): Promise<Record<string, unknown>> {
    const res = await this.request(`${this.baseUrl}/admin/templates`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(template),
    });
    return (await res.json()) as Record<string, unknown>;
  }

  public async updateTemplate(id: string, template: Record<string, unknown>): Promise<Record<string, unknown>> {
    const res = await this.request(`${this.baseUrl}/admin/templates/${encodeURIComponent(id)}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(template),
    });
    return (await res.json()) as Record<string, unknown>;
  }

  public async deleteTemplate(id: string): Promise<{ success: boolean }> {
    const res = await this.request(`${this.baseUrl}/admin/templates/${encodeURIComponent(id)}`, {
      method: 'DELETE',
    });
    return (await res.json()) as { success: boolean };
  }

  public async getDlt(): Promise<{ dlt: Record<string, unknown> }> {
    const res = await this.request(`${this.baseUrl}/admin/dlt`);
    return (await res.json()) as { dlt: Record<string, unknown> };
  }

  public async updateDlt(payload: { dlt: Record<string, unknown> }): Promise<{ dlt: Record<string, unknown> }> {
    const res = await this.request(`${this.baseUrl}/admin/dlt`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    return (await res.json()) as { dlt: Record<string, unknown> };
  }

  public async getSuppressions(): Promise<{ suppressions: Record<string, string[]> }> {
    const res = await this.request(`${this.baseUrl}/admin/suppressions`);
    return (await res.json()) as { suppressions: Record<string, string[]> };
  }

  public async updateSuppressions(payload: { suppressions: Record<string, string[]> }): Promise<unknown> {
    const res = await this.request(`${this.baseUrl}/admin/suppressions`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    return await res.json();
  }

  public async getAlerts(): Promise<{ alerts: Record<string, unknown> }> {
    const res = await this.request(`${this.baseUrl}/admin/alerts`);
    return (await res.json()) as { alerts: Record<string, unknown> };
  }

  public async updateAlerts(payload: { alerts: Record<string, unknown> }): Promise<unknown> {
    const res = await this.request(`${this.baseUrl}/admin/alerts`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    return await res.json();
  }

  public async getTeam(): Promise<{ team: Array<{ email: string; role: string }> }> {
    const res = await this.request(`${this.baseUrl}/admin/team`);
    return (await res.json()) as { team: Array<{ email: string; role: string }> };
  }

  public async getInsights(): Promise<Record<string, unknown>> {
    const res = await this.request(`${this.baseUrl}/admin/insights`);
    return (await res.json()) as Record<string, unknown>;
  }

  public async getOpenApiSpec(): Promise<Record<string, unknown>> {
    const res = await this.request(`${this.baseUrl}/admin/openapi.json`);
    return (await res.json()) as Record<string, unknown>;
  }

  public async getMessageDetail(messageId: string): Promise<Record<string, unknown>> {
    const res = await this.request(`${this.baseUrl}/admin/messages/${encodeURIComponent(messageId)}`);
    return (await res.json()) as Record<string, unknown>;
  }

  public async getLogFiles(): Promise<{ files: Array<Record<string, unknown>> }> {
    const res = await this.request(`${this.baseUrl}/admin/logs/files`);
    return (await res.json()) as { files: Array<Record<string, unknown>> };
  }

  public async getLogFileContent(filename: string, app?: string): Promise<{
    filename: string;
    raw: string;
    records: LogRecord[];
  }> {
    const qs = app ? `?app=${encodeURIComponent(app)}` : '';
    const res = await this.request(
      `${this.baseUrl}/admin/logs/files/${encodeURIComponent(filename)}${qs}`,
    );
    return (await res.json()) as { filename: string; raw: string; records: LogRecord[] };
  }

  public async searchLogs(params: Record<string, string | number | undefined> = {}): Promise<{
    records: LogRecord[];
    cursor?: string;
    has_more: boolean;
  }> {
    const sp = new URLSearchParams();
    Object.entries(params).forEach(([k, v]) => {
      if (v !== undefined && v !== '') sp.set(k, String(v));
    });
    const qs = sp.toString();
    const res = await this.request(
      `${this.baseUrl}/admin/logs/search${qs ? `?${qs}` : ''}`,
    );
    return (await res.json()) as { records: LogRecord[]; cursor?: string; has_more: boolean };
  }

  public async updateChannelStrategy(
    channel: string,
    strategy: 'failover' | 'round-robin',
  ): Promise<{ channel: string; strategy: string }> {
    const res = await this.request(`${this.baseUrl}/admin/channels/${encodeURIComponent(channel)}/strategy`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ strategy }),
    });
    return (await res.json()) as { channel: string; strategy: string };
  }

  public async health(): Promise<{ status: string; version: string; uptime: number; providers: Record<string, unknown> }> {
    const res = await this.request(`${this.baseUrl}/admin/health`);
    return (await res.json()) as any;
  }

  public async getApplications(): Promise<ApplicationConfig[]> {
    const res = await this.request(`${this.baseUrl}/admin/applications`);
    return (await res.json()) as ApplicationConfig[];
  }

  public async createApplication(app: Partial<ApplicationConfig> & { id: string; name: string }): Promise<ApplicationConfig> {
    const res = await this.request(`${this.baseUrl}/admin/applications`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(app),
    });
    return (await res.json()) as ApplicationConfig;
  }

  public async updateApplication(id: string, app: Partial<ApplicationConfig>): Promise<ApplicationConfig> {
    const res = await this.request(`${this.baseUrl}/admin/applications/${encodeURIComponent(id)}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(app),
    });
    return (await res.json()) as ApplicationConfig;
  }

  public async deleteApplication(id: string): Promise<{ success: boolean }> {
    const res = await this.request(`${this.baseUrl}/admin/applications/${encodeURIComponent(id)}`, {
      method: 'DELETE',
    });
    return (await res.json()) as { success: boolean };
  }

  public async generateApplicationKey(id: string): Promise<{ app_id: string; api_key: string }> {
    const res = await this.request(`${this.baseUrl}/admin/applications/${encodeURIComponent(id)}/keys`, {
      method: 'POST',
    });
    return (await res.json()) as { app_id: string; api_key: string };
  }

  public async rotateApplicationKey(id: string): Promise<{ app_id: string; api_key: string; key_prefix: string }> {
    const res = await this.request(`${this.baseUrl}/admin/applications/${encodeURIComponent(id)}/rotate-key`, {
      method: 'POST',
    });
    return (await res.json()) as { app_id: string; api_key: string; key_prefix: string };
  }

  public async patchApplication(id: string, app: Partial<ApplicationConfig>): Promise<ApplicationConfig> {
    const res = await this.request(`${this.baseUrl}/admin/applications/${encodeURIComponent(id)}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(app),
    });
    return (await res.json()) as ApplicationConfig;
  }

  public async getStats(): Promise<AdminStats> {
    const res = await this.request(`${this.baseUrl}/admin/stats`);
    return (await res.json()) as AdminStats;
  }

  public async getMessages(params: {
    q?: string;
    status?: string;
    channel?: string;
    app?: string;
    limit?: number;
    cursor?: string;
  } = {}): Promise<AdminMessage[]> {
    const sp = new URLSearchParams();
    Object.entries(params).forEach(([k, v]) => {
      if (v !== undefined && v !== '') sp.set(k, String(v));
    });
    const qs = sp.toString();
    const res = await this.request(
      `${this.baseUrl}/admin/messages${qs ? `?${qs}` : ''}`,
    );
    return (await res.json()) as AdminMessage[];
  }

  public async getProviders(): Promise<AdminProvider[]> {
    const res = await this.request(`${this.baseUrl}/admin/providers`);
    return (await res.json()) as AdminProvider[];
  }

  public async createProvider(data: {
    id?: string;
    channel: string;
    type: string;
    name: string;
    config?: Record<string, any>;
    secrets?: Record<string, any>;
  }): Promise<AdminProvider> {
    const res = await this.request(`${this.baseUrl}/admin/providers`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    return (await res.json()) as AdminProvider;
  }

  public async updateProvider(id: string, data: {
    name?: string;
    config?: Record<string, any>;
    secrets?: Record<string, any>;
    enabled?: boolean;
  }): Promise<AdminProvider> {
    const res = await this.request(`${this.baseUrl}/admin/providers/${encodeURIComponent(id)}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    return (await res.json()) as AdminProvider;
  }

  public async deleteProvider(id: string): Promise<{ success: boolean }> {
    const res = await this.request(`${this.baseUrl}/admin/providers/${encodeURIComponent(id)}`, {
      method: 'DELETE',
    });
    return (await res.json()) as { success: boolean };
  }

  public async testProvider(id: string): Promise<{ ok: boolean; latency_ms: number; error?: string }> {
    const res = await this.request(`${this.baseUrl}/admin/providers/${encodeURIComponent(id)}/test`, {
      method: 'POST',
    });
    return (await res.json()) as { ok: boolean; latency_ms: number; error?: string };
  }

  public async testSendProvider(id: string, payload: {
    to?: string;
    recipient?: string;
    subject?: string;
    body?: string;
    template_id?: string;
    app?: string;
    variables?: Record<string, string>;
  } = {}): Promise<{
    ok: boolean;
    message_id?: string;
    status: string;
    error?: string;
    latency_ms: number;
    via?: string;
  }> {
    const res = await this.request(`${this.baseUrl}/admin/providers/${encodeURIComponent(id)}/test-send`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    return (await res.json()) as any;
  }

  public async updateChannelOrder(channel: string, order: string[]): Promise<{ channel: string; order: string[] }> {
    const res = await this.request(`${this.baseUrl}/admin/channels/${encodeURIComponent(channel)}/order`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ order }),
    });
    return (await res.json()) as { channel: string; order: string[] };
  }

  public async getSettings(): Promise<AdminSettings> {
    const res = await this.request(`${this.baseUrl}/admin/settings`);
    return (await res.json()) as AdminSettings;
  }

  public async updateSettings(data: {
    retry?: { attempts?: number; wait?: number };
    circuit_breaker?: { trip?: number; pause?: number };
    retention_days?: number;
  }): Promise<{ success: boolean }> {
    const res = await this.request(`${this.baseUrl}/admin/settings`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    return (await res.json()) as { success: boolean };
  }

  public async getRouting(): Promise<RoutingConfig> {
    const res = await this.request(`${this.baseUrl}/admin/routing`);
    return (await res.json()) as RoutingConfig;
  }

  public async updateRouting(routing: RoutingConfig): Promise<RoutingConfig> {
    const res = await this.request(`${this.baseUrl}/admin/routing`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(routing),
    });
    return (await res.json()) as RoutingConfig;
  }

  public async getVaultEntries(): Promise<Array<{ provider_id: string; version: number; meta: Record<string, unknown>; rotated_at: number; uses: number }>> {
    const res = await this.request(`${this.baseUrl}/admin/vault`);
    return (await res.json()) as any;
  }

  public async setVaultSecret(
    providerId: string,
    secrets: Record<string, string | number | boolean>,
    meta?: Record<string, string | number | boolean>
  ): Promise<{ success: boolean }> {
    const res = await this.request(`${this.baseUrl}/admin/vault/${encodeURIComponent(providerId)}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ secrets, meta }),
    });
    return (await res.json()) as { success: boolean };
  }

  public async deleteVaultSecret(providerId: string): Promise<{ success: boolean }> {
    const res = await this.request(`${this.baseUrl}/admin/vault/${encodeURIComponent(providerId)}`, {
      method: 'DELETE',
    });
    return (await res.json()) as { success: boolean };
  }

  public async getLogs(params: {
    date?: string;
    channel?: string;
    status?: string;
    app?: string;
    provider?: string;
    limit?: number;
    cursor?: string;
    query?: string;
  } = {}): Promise<{ records: LogRecord[]; cursor?: string; has_more: boolean }> {
    const sp = new URLSearchParams();
    Object.entries(params).forEach(([k, v]) => {
      if (v !== undefined && v !== '') sp.set(k, String(v));
    });
    const qs = sp.toString();
    const res = await this.request(
      `${this.baseUrl}/admin/logs${qs ? `?${qs}` : ''}`,
    );
    return (await res.json()) as { records: LogRecord[]; cursor?: string; has_more: boolean };
  }

  public async getMetrics(): Promise<Record<string, unknown>> {
    const res = await this.request(`${this.baseUrl}/admin/metrics`);
    return (await res.json()) as Record<string, unknown>;
  }

  public subscribeEvents(
    onEvent: (event: SSEEvent) => void,
    onError?: (error: Error) => void
  ): () => void {
    const controller = new AbortController();
    const url = `${this.baseUrl}/admin/events`;

    (async () => {
      try {
        const headers: Record<string, string> = {
          Accept: 'text/event-stream',
        };
        if (this.adminToken) {
          headers.Authorization = `Bearer ${this.adminToken}`;
        }

        const res = await this.fetchFn(url, {
          headers,
          credentials: 'include',
          signal: controller.signal,
        });

        if (!res.ok || !res.body) {
          throw new Error(`SSE connection failed: ${res.status}`);
        }

        const reader = res.body.getReader();
        const decoder = new TextDecoder();
        let buffer = '';

        while (true) {
          const { done, value } = await reader.read();
          if (done) break;

          buffer += decoder.decode(value, { stream: true });
          const lines = buffer.split('\n\n');
          buffer = lines.pop() || '';

          for (const block of lines) {
            const rawLines = block.split('\n');
            let eventType = 'message';
            let eventId = '';
            let dataStr = '';

            for (const line of rawLines) {
              if (line.startsWith('event: ')) {
                eventType = line.slice(7).trim();
              } else if (line.startsWith('id: ')) {
                eventId = line.slice(4).trim();
              } else if (line.startsWith('data: ')) {
                dataStr = line.slice(6).trim();
              }
            }

            if (dataStr) {
              try {
                const parsed = JSON.parse(dataStr);
                onEvent({
                  id: eventId,
                  event: eventType as any,
                  data: parsed,
                  timestamp: Date.now(),
                });
              } catch {
                // Ignore unparseable frames
              }
            }
          }
        }
      } catch (err: any) {
        if (!controller.signal.aborted && onError) {
          onError(err);
        }
      }
    })();

    return () => {
      controller.abort();
    };
  }

  private async request(url: string, init: RequestInit & { skipCsrf?: boolean } = {}): Promise<Response> {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), this.timeoutMs);

    const headers: Record<string, string> = {
      ...(init.headers as Record<string, string>),
    };

    if (this.adminToken && !headers.Authorization) {
      headers.Authorization = `Bearer ${this.adminToken}`;
    }

    const method = (init.method || 'GET').toUpperCase();
    const isMutating = method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'DELETE';
    if (isMutating && !init.skipCsrf && this.csrfToken) {
      headers['X-CSRF-Token'] = this.csrfToken;
    }

    try {
      const { skipCsrf: _skipCsrf, ...fetchInit } = init;
      const response = await this.fetchFn(url, {
        ...fetchInit,
        headers,
        credentials: fetchInit.credentials ?? 'include',
        signal: controller.signal,
      });

      if (!response.ok) {
        let errorData: any = {};
        try {
          errorData = await response.json();
        } catch {
          errorData = { message: response.statusText };
        }

        const code = errorData?.error?.code || errorData?.code || 'ADMIN_REQUEST_FAILED';
        const msg = errorData?.error?.message || errorData?.message || response.statusText;
        throw new ZyphonError(msg, code, response.status, errorData);
      }

      return response;
    } catch (err: any) {
      if (err.name === 'AbortError') {
        throw new ZyphonError(`Request timed out after ${this.timeoutMs}ms`, 'TIMEOUT', 504);
      }
      if (err instanceof ZyphonError) {
        throw err;
      }
      throw new ZyphonError(err.message || 'Network error', 'INTERNAL_ERROR', 500, err);
    } finally {
      clearTimeout(timeout);
    }
  }
}
