/**
 * Next.js cache wrapper around the PubMed adapter.
 *
 * We use `unstable_cache` (not the newer `use cache` directive) because the
 * surrounding codebase isn't yet opted into Cache Components. This wrapper
 * lets us:
 *   - persist the normalised study list for 6 hours between requests
 *   - tag it `"studies"` so `/api/refresh/studies` can revalidate it
 *   - swallow fetch errors (the underlying adapter already returns [] on
 *     failure, so the cache never stores a throw)
 *
 * Note: when PubMed returns nothing or errors, we DO cache the empty array so
 * we don't hammer NCBI on every request while they're down. The refresh API
 * route provides a manual escape hatch.
 *
 * Exports:
 *   - `getCachedStudiesWithMeta()` — canonical entry point that returns
 *     `{ items, fetchedAt }`. `fetchedAt` is an ISO-8601 timestamp stamped
 *     at cache-fill time so the UI can show a "last refreshed" marker.
 *   - `getCachedStudies()` — thin backward-compatible wrapper that returns
 *     just `Study[]`. Preserved because the homepage imports this shape.
 *   - `getCachedStudyByPmid()` — convenience lookup by PMID.
 */

import { unstable_cache } from "next/cache";

import { fetchStudiesFromPubMed } from "./pubmed";
import type { Study } from "./types";

/** 6 hours, matching the spec. */
const REVALIDATE_SECONDS = 21_600;

export const STUDIES_CACHE_TAG = "studies";

export type StudiesCacheEntry = {
  items: Study[];
  /** ISO-8601 timestamp of when the cache entry was filled. */
  fetchedAt: string;
};

/**
 * Canonical cached aggregator. Returns the study list plus the timestamp
 * at which the cache entry was last filled.
 */
/**
 * Internal cached entry that only fires on a successful, non-empty fetch.
 * Throwing on empty/error ensures `unstable_cache` does NOT persist a bad
 * result — the next request tries again instead of being stuck on empty
 * for 6 hours.
 */
const getCachedStudiesInner: () => Promise<StudiesCacheEntry> = unstable_cache(
  async () => {
    const fetchedAt = new Date().toISOString();
    const items = await fetchStudiesFromPubMed();
    if (items.length === 0) {
      // Throwing prevents the empty result from being cached.
      throw new Error("pubmed returned zero studies");
    }
    return { items, fetchedAt };
  },
  ["studies:pubmed:v3"],
  {
    tags: [STUDIES_CACHE_TAG],
    revalidate: REVALIDATE_SECONDS,
  },
);

/**
 * Canonical cached aggregator. Returns the study list plus the timestamp
 * at which the cache entry was last filled. If the upstream fetch fails
 * or returns zero studies, the error is swallowed here and the page
 * renders an empty state — but nothing is cached, so the next request
 * re-tries PubMed instead of being stuck on an empty response.
 */
export async function getCachedStudiesWithMeta(): Promise<StudiesCacheEntry> {
  try {
    return await getCachedStudiesInner();
  } catch (err) {
    // eslint-disable-next-line no-console
    console.error(
      "[studies/cache] fetch failed, returning empty list (NOT cached):",
      err,
    );
    return { items: [] as Study[], fetchedAt: new Date().toISOString() };
  }
}

/**
 * Backward-compatible wrapper: returns just the study array.
 *
 * The homepage agent imports this symbol and expects `Study[]`. Do NOT
 * change the return shape here — extend `getCachedStudiesWithMeta()` instead.
 */
export async function getCachedStudies(): Promise<Study[]> {
  const { items } = await getCachedStudiesWithMeta();
  return items;
}

/** Convenience: fetch the cached list and find one entry by PMID. */
export async function getCachedStudyByPmid(pmid: string): Promise<Study | null> {
  const { items } = await getCachedStudiesWithMeta();
  return items.find((s) => s.pmid === pmid) ?? null;
}
