import {
  ApiError,
  type HealthResponse,
  type SkepticResponse,
  type LayeredResponse,
  type QuarantineResponse,
  type CircuitBreakerResponse,
  type AuditResponse,
  type ListSourcesResponse,
  type SourceImpactResponse,
  type QuarantineSourceResponse,
  type RestoreSourceResponse,
  type GetPatternsResponse,
  type ScanRequest,
  type ScanResponse,
  type ListScansResponse,
} from "./types";

export class StemeDBClient {
  private baseUrl: string;
  private apiKey: string | null;

  constructor(baseUrl?: string, apiKey?: string) {
    this.baseUrl =
      baseUrl ||
      process.env.NEXT_PUBLIC_STEMEDB_API_URL ||
      "http://127.0.0.1:18180";
    this.apiKey = apiKey || process.env.STEMEDB_API_KEY || null;
  }

  private async fetch<T>(path: string, options?: RequestInit): Promise<T> {
    const headers: HeadersInit = { "Content-Type": "application/json" };
    if (this.apiKey) {
      headers["X-API-Key"] = this.apiKey;
    }

    const response = await fetch(`${this.baseUrl}${path}`, {
      ...options,
      headers: { ...headers, ...options?.headers },
      cache: "no-store",
    });

    if (!response.ok) {
      throw new ApiError(response.status, await response.text());
    }

    return response.json();
  }

  async health(): Promise<HealthResponse> {
    return this.fetch<HealthResponse>("/health");
  }

  async skeptic(
    subject: string,
    predicate: string,
    includeSourceMetadata = true,
    asOf?: number
  ): Promise<SkepticResponse> {
    const params = new URLSearchParams({
      subject,
      predicate,
      include_source_metadata: String(includeSourceMetadata),
    });
    if (asOf !== undefined) {
      params.set("as_of", String(asOf));
    }
    return this.fetch<SkepticResponse>(`/v1/skeptic?${params}`);
  }

  async layered(
    subject: string,
    predicate: string,
    asOf?: number
  ): Promise<LayeredResponse> {
    const params = new URLSearchParams({ subject, predicate });
    if (asOf !== undefined) {
      params.set("as_of", String(asOf));
    }
    return this.fetch<LayeredResponse>(`/v1/layered?${params}`);
  }

  async quarantine(limit = 50, offset = 0): Promise<QuarantineResponse> {
    const params = new URLSearchParams({
      limit: String(limit),
    });
    // Note: offset not yet supported by backend
    void offset;
    return this.fetch<QuarantineResponse>(`/v1/admin/quarantine?${params}`);
  }

  async restoreFromQuarantine(hash: string): Promise<void> {
    await this.fetch(`/v1/admin/quarantine/${hash}/approve`, { method: "POST" });
  }

  async deleteFromQuarantine(hash: string): Promise<void> {
    await this.fetch(`/v1/admin/quarantine/${hash}/reject`, { method: "POST" });
  }

  async circuitBreakers(): Promise<CircuitBreakerResponse> {
    return this.fetch<CircuitBreakerResponse>("/v1/admin/circuit-breakers/tripped");
  }

  async resetCircuitBreaker(agentId: string): Promise<void> {
    await this.fetch(`/v1/admin/circuit-breaker/reset`, {
      method: "POST",
      body: JSON.stringify({ agent_id: agentId }),
    });
  }

  async auditQueries(params: {
    limit?: number;
    agentId?: string;
    subject?: string;
    predicate?: string;
    from?: number;
    to?: number;
  } = {}): Promise<AuditResponse> {
    const searchParams = new URLSearchParams({ limit: String(params.limit ?? 100) });
    if (params.agentId) searchParams.set("agent_id", params.agentId);
    if (params.subject) searchParams.set("subject", params.subject);
    if (params.predicate) searchParams.set("predicate", params.predicate);
    if (params.from !== undefined) searchParams.set("from", String(params.from));
    if (params.to !== undefined) searchParams.set("to", String(params.to));
    return this.fetch<AuditResponse>(`/v1/audit/queries?${searchParams}`);
  }

  // Source Registry methods
  async listSources(limit = 100): Promise<ListSourcesResponse> {
    const params = new URLSearchParams({ limit: String(limit) });
    return this.fetch<ListSourcesResponse>(`/v1/sources?${params}`);
  }

  async getSourceImpact(hash: string): Promise<SourceImpactResponse> {
    return this.fetch<SourceImpactResponse>(`/v1/sources/${hash}/impact`);
  }

  async quarantineSource(
    hash: string,
    preview: boolean,
    reason?: string
  ): Promise<QuarantineSourceResponse> {
    return this.fetch<QuarantineSourceResponse>(
      `/v1/sources/${hash}/quarantine`,
      {
        method: "POST",
        body: JSON.stringify({ preview, reason }),
      }
    );
  }

  async restoreSource(
    hash: string,
    reason?: string
  ): Promise<RestoreSourceResponse> {
    return this.fetch<RestoreSourceResponse>(`/v1/sources/${hash}/restore`, {
      method: "POST",
      body: JSON.stringify({ reason }),
    });
  }

  getSourceImpactExportUrl(hash: string, format: "csv" | "json"): string {
    return `${this.baseUrl}/v1/sources/${hash}/impact/export?format=${format}`;
  }

  getApiKey(): string | null {
    return this.apiKey;
  }

  // Aphoria methods
  async getPatterns(params: {
    subjectPrefix?: string;
    minProjects?: number;
    limit?: number;
  } = {}): Promise<GetPatternsResponse> {
    const searchParams = new URLSearchParams();
    if (params.subjectPrefix) searchParams.set("subject_prefix", params.subjectPrefix);
    if (params.minProjects !== undefined) searchParams.set("min_projects", String(params.minProjects));
    if (params.limit !== undefined) searchParams.set("limit", String(params.limit));
    const query = searchParams.toString();
    return this.fetch<GetPatternsResponse>(`/v1/aphoria/patterns${query ? `?${query}` : ""}`);
  }

  async runScan(request: ScanRequest): Promise<ScanResponse> {
    return this.fetch<ScanResponse>("/v1/aphoria/scan", {
      method: "POST",
      body: JSON.stringify(request),
    });
  }

  async listScans(): Promise<ListScansResponse> {
    return this.fetch<ListScansResponse>("/v1/aphoria/scans");
  }
}

// Singleton client for server components
let _client: StemeDBClient | null = null;

export function getClient(): StemeDBClient {
  if (!_client) {
    _client = new StemeDBClient();
  }
  return _client;
}
