import type {
  AgentProfile,
  AnomalyReport,
  CausalEdge,
  DreamCycleResult,
  EpisodicMemory,
  Goal,
  PredictedRecall,
  ShapeAnalysis,
  TemporalTrend,
  Timeline,
} from "./types";

type RequestOptions = {
  method?: "GET" | "POST" | "DELETE" | "PATCH";
  body?: Record<string, unknown>;
};

export class ChronosClient {
  private baseUrl: string;
  private token: string;

  constructor(baseUrl = "http://localhost:8420", token = "") {
    this.baseUrl = baseUrl.replace(/\/+$/, "");
    this.token = token;
  }

  private async request<T = unknown>(path: string, options: RequestOptions = {}): Promise<T> {
    const headers: Record<string, string> = {
      "Content-Type": "application/json",
    };
    if (this.token) {
      headers["Authorization"] = `Bearer ${this.token}`;
    }

    const response = await fetch(`${this.baseUrl}${path}`, {
      method: options.method ?? "GET",
      headers,
      body: options.body ? JSON.stringify(options.body) : undefined,
    });

    if (!response.ok) {
      const text = await response.text();
      throw new Error(`CHRONOS request failed (${response.status}): ${text}`);
    }
    return (await response.json()) as T;
  }

  // ===== v0.6 Agent Memory =====

  async registerAgent(name: string, domains: string[] = []): Promise<AgentProfile> {
    return this.request<AgentProfile>("/api/v5/agents", {
      method: "POST",
      body: { name, expertise_domains: domains },
    });
  }

  async listAgents(): Promise<AgentProfile[]> {
    const payload = await this.request<{ agents: AgentProfile[] }>("/api/v5/agents");
    return payload.agents ?? [];
  }

  async getAgent(agentId: string): Promise<AgentProfile> {
    return this.request<AgentProfile>(`/api/v5/agents/${encodeURIComponent(agentId)}`);
  }

  async storeEpisode(
    agentId: string,
    content: string,
    options: { location?: string; importance?: number; emotional_valence?: number } = {},
  ): Promise<EpisodicMemory> {
    return this.request<EpisodicMemory>(`/api/v5/agents/${encodeURIComponent(agentId)}/episodes`, {
      method: "POST",
      body: { content, ...options },
    });
  }

  async recallEpisodes(
    agentId: string,
    options: { since_ms?: number; min_importance?: number; limit?: number } = {},
  ): Promise<EpisodicMemory[]> {
    const params = new URLSearchParams();
    if (options.since_ms !== undefined) params.set("since_ms", String(options.since_ms));
    if (options.min_importance !== undefined) params.set("min_importance", String(options.min_importance));
    if (options.limit !== undefined) params.set("limit", String(options.limit));
    const payload = await this.request<{ episodes: EpisodicMemory[] }>(
      `/api/v5/agents/${encodeURIComponent(agentId)}/episodes?${params.toString()}`,
    );
    return payload.episodes ?? [];
  }

  async consolidateSemantics(agentId: string): Promise<{ created: number; concepts: string[] }> {
    return this.request(`/api/v5/agents/${encodeURIComponent(agentId)}/consolidate`, {
      method: "POST",
      body: {},
    });
  }

  // ===== v0.6 Dream =====

  async dream(agentId: string, lucid = false): Promise<DreamCycleResult> {
    return this.request<DreamCycleResult>(`/api/v5/agents/${encodeURIComponent(agentId)}/dream`, {
      method: "POST",
      body: { lucid_mode: lucid },
    });
  }

  async dreamHistory(agentId: string, limit = 10): Promise<DreamCycleResult[]> {
    const payload = await this.request<{ history: DreamCycleResult[] }>(
      `/api/v5/agents/${encodeURIComponent(agentId)}/dream/history?limit=${encodeURIComponent(String(limit))}`,
    );
    return payload.history ?? [];
  }

  // ===== v0.6 Defense =====

  async defenseStatus(): Promise<Record<string, unknown>> {
    return this.request("/api/v5/defense/status");
  }

  async embeddingDrift(): Promise<{ drift_magnitude: number; status: string }> {
    return this.request("/api/v5/defense/drift");
  }

  async quarantineList(): Promise<AnomalyReport[]> {
    const payload = await this.request<{ items: AnomalyReport[] }>("/api/v5/defense/quarantine");
    return payload.items ?? [];
  }

  // ===== v0.6 Goals =====

  async extractGoals(agentId: string): Promise<Goal[]> {
    const payload = await this.request<{ goals: Goal[] }>(`/api/v5/agents/${encodeURIComponent(agentId)}/goals/extract`, {
      method: "POST",
      body: {},
    });
    return payload.goals ?? [];
  }

  async listGoals(agentId: string, status = ""): Promise<Goal[]> {
    const suffix = status ? `?status=${encodeURIComponent(status)}` : "";
    const payload = await this.request<{ goals: Goal[] }>(
      `/api/v5/agents/${encodeURIComponent(agentId)}/goals${suffix}`,
    );
    return payload.goals ?? [];
  }

  async planGoal(agentId: string, goalId: string): Promise<Goal> {
    return this.request<Goal>(`/api/v5/agents/${encodeURIComponent(agentId)}/goals/${encodeURIComponent(goalId)}/plan`, {
      method: "POST",
      body: {},
    });
  }

  // ===== v0.7 Temporal =====

  async buildTimeline(
    agentId: string,
    query: string,
    options: { granularity?: string; window_days?: number } = {},
  ): Promise<Timeline> {
    return this.request<Timeline>("/api/v5/temporal/timeline", {
      method: "POST",
      body: { agent_id: agentId, query, ...options },
    });
  }

  async temporalDiff(
    agentId: string,
    periodA: [number, number],
    periodB: [number, number],
  ): Promise<{ new_topics: string[]; lost_topics: string[]; importance_shift: number; summary: string }> {
    return this.request("/api/v5/temporal/diff", {
      method: "POST",
      body: {
        agent_id: agentId,
        period_a: periodA,
        period_b: periodB,
      },
    });
  }

  async temporalQuery(agentId: string, question: string): Promise<string> {
    const response = await this.request<{ answer: string }>("/api/v5/temporal/query", {
      method: "POST",
      body: { agent_id: agentId, question },
    });
    return response.answer;
  }

  async temporalTrends(agentId: string, limit = 20): Promise<TemporalTrend[]> {
    const payload = await this.request<{ trends: TemporalTrend[] }>(
      `/api/v5/temporal/trends?agent_id=${encodeURIComponent(agentId)}&limit=${encodeURIComponent(String(limit))}`,
    );
    return payload.trends ?? [];
  }

  // ===== v0.7 Causal =====

  async rootCauseAnalysis(
    agentId: string,
    symptom: string,
  ): Promise<Array<{ cause: string; confidence: number; mechanism: string }>> {
    const payload = await this.request<{ causes: Array<{ cause: string; confidence: number; mechanism: string }> }>(
      `/api/v5/agents/${encodeURIComponent(agentId)}/causal/root-cause`,
      {
        method: "POST",
        body: { symptom },
      },
    );
    return payload.causes ?? [];
  }

  async counterfactual(
    agentId: string,
    original: string,
    alternative: string,
  ): Promise<{ reasoning: string; confidence: number; predicted_changes: Array<Record<string, unknown>> }> {
    return this.request(`/api/v5/agents/${encodeURIComponent(agentId)}/causal/counterfactual`, {
      method: "POST",
      body: {
        original_event: original,
        alternative,
      },
    });
  }

  async discoverCauses(agentId: string, memoryId: string): Promise<CausalEdge[]> {
    const payload = await this.request<{ edges: CausalEdge[] }>(
      `/api/v5/agents/${encodeURIComponent(agentId)}/causal/discover`,
      {
        method: "POST",
        body: { memory_id: memoryId },
      },
    );
    return payload.edges ?? [];
  }

  // ===== v0.7 Morphology =====

  async detectShape(agentId: string, topic = ""): Promise<ShapeAnalysis> {
    return this.request<ShapeAnalysis>(
      `/api/v5/agents/${encodeURIComponent(agentId)}/morphology/shape?topic=${encodeURIComponent(topic)}`,
    );
  }

  async topologyMap(agentId: string): Promise<Record<string, unknown>> {
    return this.request(`/api/v5/agents/${encodeURIComponent(agentId)}/morphology/topology`);
  }

  // ===== v0.7 Predictive =====

  async predictRecall(agentId: string): Promise<PredictedRecall[]> {
    const payload = await this.request<{ predictions: PredictedRecall[] }>(
      `/api/v5/agents/${encodeURIComponent(agentId)}/predict`,
    );
    return payload.predictions ?? [];
  }
}

export * from "./types";
