const GRAPH_BASE = "https://graph.threads.net/v1.0";

export class ThreadsApiError extends Error {
  constructor(
    message: string,
    public readonly code?: number,
    public readonly subcode?: number
  ) {
    super(message);
    this.name = "ThreadsApiError";
  }
}

async function parseJson(res: Response): Promise<Record<string, unknown>> {
  const text = await res.text();
  try {
    return JSON.parse(text) as Record<string, unknown>;
  } catch {
    throw new ThreadsApiError(text || `HTTP ${res.status}`, res.status);
  }
}

export async function threadsGraphGet<T>(
  path: string,
  params: Record<string, string>
): Promise<T> {
  const url = new URL(`${GRAPH_BASE}${path}`);
  for (const [key, value] of Object.entries(params)) {
    url.searchParams.set(key, value);
  }

  const res = await fetch(url.toString(), { method: "GET" });
  const json = await parseJson(res);

  if (!res.ok) {
    throw new ThreadsApiError(
      String(json.error_message ?? json.error ?? "Threads API 請求失敗"),
      typeof json.code === "number" ? json.code : res.status
    );
  }

  return json as T;
}

export async function threadsGraphPost<T>(
  path: string,
  params: Record<string, string>
): Promise<T> {
  const body = new URLSearchParams(params);
  const res = await fetch(`${GRAPH_BASE}${path}`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body,
  });

  const json = await parseJson(res);
  if (!res.ok) {
    throw new ThreadsApiError(
      String(json.error_message ?? json.error ?? "Threads API 請求失敗"),
      typeof json.code === "number" ? json.code : res.status
    );
  }

  return json as T;
}
