const DEFAULT_CLIENT_KEY = "9ca7f706-a8b0-4520-b467-5e8321df36fb";

function clientKey() {
  return process.env.NEXT_PUBLIC_X_CLIENT_KEY ?? DEFAULT_CLIENT_KEY;
}

function apiBase() {
  const base = process.env.NEXT_PUBLIC_API_URL?.replace(/\/$/, "");
  return base ?? "";
}

/** Article shape returned by public GET /articles and GET /articles/:id (camelCase JSON). */
export type PublicArticle = {
  id: number;
  title: string;
  slug: string;
  description: string;
  htmlDescription?: string;
  categoryName?: string;
  typeId: number;
  tags?: string;
  thumbnailUrl?: string;
  viewCount?: number | null;
  publishedAt?: string | null;
  createdAt: string;
  isPublish?: boolean | null;
  files?: Array<{
    fileUrl?: string | null;
    fileName?: string | null;
  }>;
};

export type ArticlesListResult = {
  items: PublicArticle[];
  meta?: { totalPage?: number; count?: number };
};

type FetchMode = "server" | "client";

async function articlesFetchJson(
  path: string,
  mode: FetchMode = "server",
): Promise<{
  data: unknown;
  meta?: ArticlesListResult["meta"];
} | null> {
  const base = apiBase();
  if (!base) return null;
  const url = `${base}${path.startsWith("/") ? path : `/${path}`}`;
  try {
    const res = await fetch(url, {
      headers: {
        "Content-Type": "application/json",
        "X-Client-Key": clientKey(),
      },
      ...(mode === "server"
        ? { next: { revalidate: 60 } }
        : { cache: "no-store" as RequestCache }),
    });
    if (!res.ok) return null;
    const json = (await res.json()) as {
      data?: unknown;
      meta?: ArticlesListResult["meta"];
    };
    return { data: json.data, meta: json.meta };
  } catch {
    return null;
  }
}

export async function fetchPublishedArticles(
  params: {
    typeId?: number;
    limit?: number;
    page?: number;
    sortBy?: string;
    sort?: string;
    title?: string;
    /** Partial match on `articles.tags` (comma-separated string in DB). */
    tags?: string;
  },
  options?: { mode?: FetchMode },
): Promise<ArticlesListResult | null> {
  const sp = new URLSearchParams();
  sp.set("limit", String(params.limit ?? 8));
  sp.set("page", String(params.page ?? 1));
  sp.set("isPublish", "true");
  sp.set("sortBy", params.sortBy ?? "created_at");
  sp.set("sort", params.sort ?? "desc");
  if (params.typeId != null) sp.set("typeId", String(params.typeId));
  if (params.title) sp.set("title", params.title);
  if (params.tags) sp.set("tags", params.tags);
  const mode = options?.mode ?? "server";
  const raw = await articlesFetchJson(`/articles?${sp.toString()}`, mode);
  if (!raw) return null;
  const items = Array.isArray(raw.data)
    ? (raw.data as PublicArticle[])
    : [];
  return { items, meta: raw.meta };
}

export async function fetchArticlePublic(
  id: number,
  options?: { mode?: FetchMode },
): Promise<PublicArticle | null> {
  const mode = options?.mode ?? "server";
  const raw = await articlesFetchJson(`/articles/${id}`, mode);
  if (raw?.data == null || typeof raw.data !== "object") return null;
  return raw.data as PublicArticle;
}

/** Count tags (comma-separated on articles) for a “popular tags” list. */
export function aggregateTagStats(
  articles: PublicArticle[],
  topN = 8,
): { name: string; count: number }[] {
  const counts = new Map<string, { display: string; count: number }>();
  for (const a of articles) {
    if (!a.tags?.trim()) continue;
    for (const raw of a.tags.split(",")) {
      const display = raw.trim();
      if (!display) continue;
      const key = display.toLowerCase();
      const cur = counts.get(key);
      if (cur) cur.count += 1;
      else counts.set(key, { display, count: 1 });
    }
  }
  return [...counts.values()]
    .sort((a, b) => b.count - a.count)
    .slice(0, topN)
    .map(({ display, count }) => ({ name: display, count }));
}
