import type {
  Agent,
  ApiKey,
  Approval,
  AuditEntry,
  Connector,
  ConnectorType,
  DocumentRow,
  KnowledgeBase,
  Member,
  Role,
  Run,
  Step,
  Tool,
  Workflow,
  Workspace,
} from "./types";

export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8000";

const ACCESS_KEY = "legate.access";
const REFRESH_KEY = "legate.refresh";

export function getAccessToken(): string | null {
  if (typeof window === "undefined") return null;
  return window.localStorage.getItem(ACCESS_KEY);
}
export function getRefreshToken(): string | null {
  if (typeof window === "undefined") return null;
  return window.localStorage.getItem(REFRESH_KEY);
}
export function setTokens(access: string, refresh?: string) {
  window.localStorage.setItem(ACCESS_KEY, access);
  if (refresh) window.localStorage.setItem(REFRESH_KEY, refresh);
}
export function clearTokens() {
  window.localStorage.removeItem(ACCESS_KEY);
  window.localStorage.removeItem(REFRESH_KEY);
}

export class ApiError extends Error {
  status: number;
  constructor(status: number, message: string) {
    super(message);
    this.status = status;
  }
}

async function parseError(res: Response): Promise<string> {
  try {
    const data = await res.json();
    if (typeof data.detail === "string") return data.detail;
    if (Array.isArray(data.detail))
      return data.detail.map((d: { msg?: string }) => d.msg).join(", ");
    return JSON.stringify(data);
  } catch {
    return res.statusText || `HTTP ${res.status}`;
  }
}

async function refreshAccess(): Promise<boolean> {
  const refresh = getRefreshToken();
  if (!refresh) return false;
  const res = await fetch(`${API_BASE_URL}/auth/refresh`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ refresh_token: refresh }),
  });
  if (!res.ok) return false;
  const data = await res.json();
  setTokens(data.access_token, data.refresh_token);
  return true;
}

interface RequestOptions {
  method?: string;
  body?: unknown;
  auth?: boolean;
  raw?: boolean; // send body as-is (FormData)
}

export async function apiFetch<T>(path: string, opts: RequestOptions = {}): Promise<T> {
  const { method = "GET", body, auth = true, raw = false } = opts;

  const build = (): RequestInit => {
    const headers: Record<string, string> = {};
    if (auth) {
      const token = getAccessToken();
      if (token) headers["Authorization"] = `Bearer ${token}`;
    }
    let payload: BodyInit | undefined;
    if (body !== undefined) {
      if (raw) {
        payload = body as BodyInit;
      } else {
        headers["Content-Type"] = "application/json";
        payload = JSON.stringify(body);
      }
    }
    return { method, headers, body: payload };
  };

  let res = await fetch(`${API_BASE_URL}${path}`, build());
  if (res.status === 401 && auth && (await refreshAccess())) {
    res = await fetch(`${API_BASE_URL}${path}`, build());
  }
  if (!res.ok) {
    const message = await parseError(res);
    if (res.status === 401) clearTokens();
    throw new ApiError(res.status, message);
  }
  if (res.status === 204) return undefined as T;
  return (await res.json()) as T;
}

export interface TokenResponse {
  access_token: string;
  refresh_token: string;
  workspace_id: string | null;
}

export const api = {
  // -- auth --
  register: (email: string, password: string, name: string) =>
    apiFetch<{ id: string }>("/auth/register", {
      method: "POST",
      auth: false,
      body: { email, password, name },
    }),
  login: (email: string, password: string) =>
    apiFetch<TokenResponse>("/auth/login", {
      method: "POST",
      auth: false,
      body: { email, password },
    }),

  // -- workspaces --
  listWorkspaces: () => apiFetch<Workspace[]>("/workspaces"),
  createWorkspace: (name: string) =>
    apiFetch<{ workspace: Workspace; access_token: string }>("/workspaces", {
      method: "POST",
      body: { name },
    }),
  switchWorkspace: (id: string) =>
    apiFetch<{ access_token: string; workspace_id: string }>(`/workspaces/${id}/switch`, {
      method: "POST",
    }),

  // -- members / admin --
  listMembers: () => apiFetch<Member[]>("/members"),
  invite: (email: string, role: Role) =>
    apiFetch<Member>("/members/invite", { method: "POST", body: { email, role } }),
  setRole: (membershipId: string, role: Role) =>
    apiFetch<Member>(`/members/${membershipId}/role`, { method: "PATCH", body: { role } }),
  listApiKeys: () => apiFetch<ApiKey[]>("/api-keys"),
  createApiKey: (name: string, role: Role) =>
    apiFetch<{ api_key: ApiKey; plaintext: string }>("/api-keys", {
      method: "POST",
      body: { name, role },
    }),
  revokeApiKey: (id: string) => apiFetch<void>(`/api-keys/${id}`, { method: "DELETE" }),

  // -- tools / agents --
  listTools: () => apiFetch<Tool[]>("/tools"),
  listAgents: () => apiFetch<Agent[]>("/agents"),
  getAgent: (id: string) => apiFetch<Agent>(`/agents/${id}`),
  createAgent: (body: Partial<Agent>) => apiFetch<Agent>("/agents", { method: "POST", body }),
  updateAgent: (id: string, body: Partial<Agent>) =>
    apiFetch<Agent>(`/agents/${id}`, { method: "PATCH", body }),
  deleteAgent: (id: string) => apiFetch<void>(`/agents/${id}`, { method: "DELETE" }),
  runAgent: (id: string, input: string) =>
    apiFetch<{ run: Run; stream_url: string }>(`/agents/${id}/run`, {
      method: "POST",
      body: { input },
    }),

  // -- runs --
  listRuns: () => apiFetch<Run[]>("/runs"),
  getRun: (id: string) => apiFetch<Run>(`/runs/${id}`),
  listSteps: (id: string) => apiFetch<Step[]>(`/runs/${id}/steps`),

  // -- approvals --
  listApprovals: (status?: string) =>
    apiFetch<Approval[]>(`/approvals${status ? `?status=${status}` : ""}`),
  approve: (id: string, reason = "") =>
    apiFetch<{ approval: Approval; run_status: string }>(`/approvals/${id}/approve`, {
      method: "POST",
      body: { reason },
    }),
  reject: (id: string, reason = "") =>
    apiFetch<{ approval: Approval; run_status: string }>(`/approvals/${id}/reject`, {
      method: "POST",
      body: { reason },
    }),

  // -- connectors --
  listConnectorTypes: () => apiFetch<ConnectorType[]>("/connector-types"),
  listConnectors: () => apiFetch<Connector[]>("/connectors"),
  installConnector: (type: string, name: string, config: object, secrets: object) =>
    apiFetch<Connector>("/connectors", { method: "POST", body: { type, name, config, secrets } }),
  testConnector: (id: string) =>
    apiFetch<{ ok: boolean; message: string; status: string }>(`/connectors/${id}/test`, {
      method: "POST",
    }),
  deleteConnector: (id: string) => apiFetch<void>(`/connectors/${id}`, { method: "DELETE" }),

  // -- knowledge --
  listKnowledgeBases: () => apiFetch<KnowledgeBase[]>("/knowledge-bases"),
  createKnowledgeBase: (name: string, description: string) =>
    apiFetch<KnowledgeBase>("/knowledge-bases", { method: "POST", body: { name, description } }),
  listDocuments: (kbId: string) => apiFetch<DocumentRow[]>(`/knowledge-bases/${kbId}/documents`),
  uploadDocument: (kbId: string, file: File) => {
    const form = new FormData();
    form.append("file", file);
    return apiFetch<DocumentRow>(`/knowledge-bases/${kbId}/documents`, {
      method: "POST",
      body: form,
      raw: true,
    });
  },
  deleteDocument: (id: string) => apiFetch<void>(`/documents/${id}`, { method: "DELETE" }),
  searchKb: (kbId: string, query: string) =>
    apiFetch<{ query: string; results: Array<{ source: string; text: string; score: number }> }>(
      `/knowledge-bases/${kbId}/search`,
      { method: "POST", body: { query, top_k: 5 } }
    ),

  // -- workflows --
  listWorkflows: () => apiFetch<Workflow[]>("/workflows"),
  getWorkflow: (id: string) => apiFetch<Workflow>(`/workflows/${id}`),
  createWorkflow: (body: {
    name: string;
    description?: string;
    definition: object;
    trigger_type: string;
    trigger_config?: object;
  }) =>
    apiFetch<{ workflow: Workflow; webhook_url: string | null; webhook_secret: string | null }>(
      "/workflows",
      { method: "POST", body }
    ),
  deleteWorkflow: (id: string) => apiFetch<void>(`/workflows/${id}`, { method: "DELETE" }),
  runWorkflow: (id: string, input: object) =>
    apiFetch<{ run: Run; stream_url: string }>(`/workflows/${id}/run`, {
      method: "POST",
      body: { input },
    }),

  // -- audit --
  listAudit: (limit = 25, offset = 0) =>
    apiFetch<AuditEntry[]>(`/audit?limit=${limit}&offset=${offset}`),
};
