import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { createClient } from "@supabase/supabase-js";
import { buildTopicCatalog } from "@/lib/topicCatalog";
import { extractTopics, normalizeList, FEATURED_TOPICS } from "@/lib/devotional";
import { topicToSlug } from "@/lib/topics";

const HOST = "uncovered-treasure-v1.p.rapidapi.com";
const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;

function admin() {
  return createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!, {
    auth: { autoRefreshToken: false, persistSession: false },
  });
}

async function rapidFetch(path: string): Promise<unknown> {
  const apiKey = process.env.RAPIDAPI_KEY;
  if (!apiKey) return null;
  try {
    const res = await fetch(`https://${HOST}${path}`, {
      headers: { "x-rapidapi-key": apiKey, "x-rapidapi-host": HOST },
    });
    if (!res.ok) return null;
    return await res.json();
  } catch (e) {
    console.error("rapidFetch failed", path, e);
    return null;
  }
}

const FEATURED_SLUGS = new Set(FEATURED_TOPICS.map(topicToSlug));

type DevotionalRow = {
  topic_slug: string;
  api_topic: string;
  scripture_reference: string | null;
  context: string | null;
  scriptures: unknown;
  devotional_text: string | null;
  topics: unknown;
  book_order: number | null;
  api_date: string | null;
  raw_response: unknown;
};

function pickStr(r: Record<string, unknown>, ...keys: string[]): string | null {
  for (const k of keys) {
    const v = r[k];
    if (typeof v === "string" && v.trim()) return v.trim();
  }
  return null;
}

function rawToRow(slug: string, apiTopic: string, raw: unknown): DevotionalRow {
  const r = (raw && typeof raw === "object" ? raw : {}) as Record<string, unknown>;
  return {
    topic_slug: slug,
    api_topic: apiTopic,
    scripture_reference: pickStr(r, "scripture_reference", "reference"),
    context: pickStr(r, "context"),
    scriptures: Array.isArray(r.scriptures) ? r.scriptures : [],
    devotional_text: pickStr(r, "text", "devotional", "insight", "body", "content"),
    topics: Array.isArray(r.topics) ? r.topics : [],
    book_order: typeof r.bookOrder === "number" ? r.bookOrder : null,
    api_date: pickStr(r, "date"),
    raw_response: r,
  };
}

async function upsertCatalog(): Promise<{ total: number; upserted: number }> {
  const data = await rapidFetch("/topics");
  if (!data) return { total: 0, upserted: 0 };
  const entries = buildTopicCatalog(extractTopics(data));
  const sb = admin();
  const rows = entries.map((e) => ({
    slug: e.slug,
    canonical_api_topic: e.canonicalApiTopic,
    display_name: e.displayName,
    aliases: e.aliases,
    is_featured: FEATURED_SLUGS.has(e.slug),
  }));
  let upserted = 0;
  for (let i = 0; i < rows.length; i += 200) {
    const chunk = rows.slice(i, i + 200);
    const { error } = await sb.from("topics").upsert(chunk, { onConflict: "slug" });
    if (error) {
      console.error("topics upsert error", error);
      break;
    }
    upserted += chunk.length;
  }
  return { total: entries.length, upserted };
}

async function syncSlug(slug: string): Promise<{ apiTopic: string; count: number; attempts: { apiTopic: string; count: number }[] } | null> {
  const sb = admin();
  const { data: topic } = await sb.from("topics")
    .select("slug, canonical_api_topic, aliases").eq("slug", slug).maybeSingle();
  if (!topic) return null;
  const aliases = Array.isArray(topic.aliases) ? (topic.aliases as string[]) : [];
  const candidates = [topic.canonical_api_topic, ...aliases];

  const attempts: { apiTopic: string; count: number }[] = [];
  let chosen: unknown = null;
  let chosenTopic = topic.canonical_api_topic as string;
  for (const t of candidates) {
    const r = await rapidFetch(`/topic/${encodeURIComponent(t)}`);
    const list = normalizeList(r);
    attempts.push({ apiTopic: t, count: list.length });
    if (list.length > 0) { chosen = r; chosenTopic = t; break; }
    if (!chosen) chosen = r;
  }

  await sb.from("topic_devotionals").delete().eq("topic_slug", slug);
  const rawList = Array.isArray((chosen as { results?: unknown[] } | null)?.results)
    ? ((chosen as { results: unknown[] }).results)
    : [];
  const insertRows = rawList
    .map((raw) => rawToRow(slug, chosenTopic, raw))
    .filter((x) => !!x.devotional_text);
  if (insertRows.length > 0) {
    const { error } = await sb.from("topic_devotionals").insert(insertRows);
    if (error) console.error("topic_devotionals insert error", error);
  }
  await sb.from("topics").update({
    result_count: insertRows.length,
    is_indexable: insertRows.length > 0,
    last_fetched_at: new Date().toISOString(),
  }).eq("slug", slug);
  return { apiTopic: chosenTopic, count: insertRows.length, attempts };
}

export const syncTopicCatalog = createServerFn({ method: "POST" }).handler(async () => {
  const r = await upsertCatalog();
  return { ok: r.total > 0, ...r };
});

export const syncTopicContent = createServerFn({ method: "POST" })
  .inputValidator(z.object({ slug: z.string().min(1).max(160) }))
  .handler(async ({ data }) => {
    const r = await syncSlug(data.slug);
    if (!r) return { ok: false, error: "Topic not found" };
    return { ok: true, slug: data.slug, ...r };
  });

export const syncAllTopicContent = createServerFn({ method: "POST" })
  .inputValidator(
    z.object({
      limit: z.number().int().min(1).max(200).optional(),
      olderThanMs: z.number().int().optional(),
    }).optional(),
  )
  .handler(async ({ data }) => {
    const sb = admin();
    const limit = data?.limit ?? 25;
    const cutoff = new Date(Date.now() - (data?.olderThanMs ?? SEVEN_DAYS_MS)).toISOString();
    const { data: rows } = await sb.from("topics")
      .select("slug")
      .or(`last_fetched_at.is.null,last_fetched_at.lt.${cutoff}`)
      .order("last_fetched_at", { ascending: true, nullsFirst: true })
      .limit(limit);
    const details: { slug: string; count: number }[] = [];
    for (const r of rows ?? []) {
      const res = await syncSlug(r.slug);
      if (res) details.push({ slug: r.slug, count: res.count });
    }
    return { ok: true, synced: details.length, details };
  });

export type TopicPageData = {
  found: boolean;
  indexable: boolean;
  slug: string;
  displayName: string;
  canonicalApiTopic: string;
  aliases: string[];
  devotionals: {
    id: string;
    scripture_reference: string | null;
    context: string | null;
    scriptures: string[];
    text: string;
    topics: string[];
    bookOrder: number | null;
  }[];
  lastFetchedAt: string | null;
};

async function readTopicPage(slug: string): Promise<TopicPageData | null> {
  const sb = admin();
  const { data: topic } = await sb.from("topics")
    .select("slug, canonical_api_topic, display_name, aliases, is_indexable, last_fetched_at")
    .eq("slug", slug).maybeSingle();
  if (!topic) return null;
  const { data: devs } = await sb.from("topic_devotionals")
    .select("id, scripture_reference, context, scriptures, devotional_text, topics, book_order")
    .eq("topic_slug", slug)
    .order("book_order", { ascending: true, nullsFirst: false });
  return {
    found: true,
    indexable: !!topic.is_indexable,
    slug: topic.slug as string,
    displayName: topic.display_name as string,
    canonicalApiTopic: topic.canonical_api_topic as string,
    aliases: Array.isArray(topic.aliases) ? (topic.aliases as string[]) : [],
    devotionals: (devs ?? []).map((d) => ({
      id: d.id as string,
      scripture_reference: (d.scripture_reference as string | null) ?? null,
      context: (d.context as string | null) ?? null,
      scriptures: Array.isArray(d.scriptures) ? (d.scriptures as string[]) : [],
      text: (d.devotional_text as string | null) ?? "",
      topics: Array.isArray(d.topics) ? (d.topics as string[]) : [],
      bookOrder: (d.book_order as number | null) ?? null,
    })),
    lastFetchedAt: (topic.last_fetched_at as string | null) ?? null,
  };
}

export const getTopicPage = createServerFn({ method: "GET" })
  .inputValidator(z.object({ slug: z.string().min(1).max(160) }))
  .handler(async ({ data }): Promise<TopicPageData> => {
    const sb = admin();
    // Ensure catalog exists on first read
    const { count } = await sb.from("topics").select("slug", { count: "exact", head: true });
    if (!count) await upsertCatalog();

    let page = await readTopicPage(data.slug);
    if (!page) {
      return {
        found: false, indexable: false, slug: data.slug,
        displayName: data.slug, canonicalApiTopic: data.slug, aliases: [],
        devotionals: [], lastFetchedAt: null,
      };
    }

    const stale = !page.lastFetchedAt ||
      Date.now() - new Date(page.lastFetchedAt).getTime() > SEVEN_DAYS_MS;
    if (stale || page.devotionals.length === 0) {
      await syncSlug(data.slug);
      page = (await readTopicPage(data.slug)) ?? page;
    }
    return page;
  });

export const getIndexableTopics = createServerFn({ method: "GET" }).handler(async () => {
  const sb = admin();
  // Ensure catalog exists
  const { count } = await sb.from("topics").select("slug", { count: "exact", head: true });
  if (!count) await upsertCatalog();
  const { data } = await sb.from("topics")
    .select("slug, display_name, is_featured, result_count, is_indexable, last_fetched_at")
    .order("display_name", { ascending: true });
  return { topics: data ?? [] };
});

export type TopicStats = {
  totalCanonical: number;
  totalAliases: number;
  indexableCount: number;
  nonIndexableCount: number;
  zeroResultsCount: number;
  neverFetchedCount: number;
  topRows: {
    slug: string;
    canonical_api_topic: string;
    display_name: string;
    result_count: number;
    is_indexable: boolean;
    last_fetched_at: string | null;
    alias_count: number;
  }[];
};

export const getTopicStats = createServerFn({ method: "GET" }).handler(async (): Promise<TopicStats> => {
  const sb = admin();
  const { data: all } = await sb.from("topics")
    .select("slug, canonical_api_topic, display_name, aliases, result_count, is_indexable, last_fetched_at");
  const rows = all ?? [];
  const totalCanonical = rows.length;
  let totalAliases = 0, indexableCount = 0, zeroResultsCount = 0, neverFetchedCount = 0;
  for (const r of rows) {
    const aCount = Array.isArray(r.aliases) ? (r.aliases as unknown[]).length : 0;
    totalAliases += aCount;
    if (r.is_indexable) indexableCount += 1;
    if (r.last_fetched_at && (r.result_count ?? 0) === 0) zeroResultsCount += 1;
    if (!r.last_fetched_at) neverFetchedCount += 1;
  }
  const topRows = [...rows]
    .sort((a, b) => (b.result_count ?? 0) - (a.result_count ?? 0))
    .slice(0, 100)
    .map((r) => ({
      slug: r.slug as string,
      canonical_api_topic: r.canonical_api_topic as string,
      display_name: r.display_name as string,
      result_count: (r.result_count as number) ?? 0,
      is_indexable: !!r.is_indexable,
      last_fetched_at: (r.last_fetched_at as string | null) ?? null,
      alias_count: Array.isArray(r.aliases) ? (r.aliases as unknown[]).length : 0,
    }));
  return {
    totalCanonical,
    totalAliases,
    indexableCount,
    nonIndexableCount: totalCanonical - indexableCount,
    zeroResultsCount,
    neverFetchedCount,
    topRows,
  };
});
