import { existsSync } from "fs";
import { join } from "path";
import { defaultContent, type HomeContent, type Product, type Feature, type Stat, type Partner, type Review, type Faq, type PageContent, type MenuItem, type BreadcrumbItem, socials } from "./content";

const WP_URL = process.env.WORDPRESS_URL?.replace(/\/$/, "");
const GRAPHQL_URL = WP_URL ? `${WP_URL}/graphql` : null;
const REVALIDATE_SECONDS = 300;

const SITE_URL = "https://sigmataxpro.com";

/** Replace any reference to the staging WordPress domain with the production domain in URLs. */
function rewriteUrl(url: string): string {
  return url.replace(/https?:\/\/dashstp\.sigmataxpro\.com/g, SITE_URL);
}

/** Convert a WordPress image URL to a local WebP path if the file exists in /public/img/. */
function wpImageToLocal(url: string): string {
  if (!url || !url.includes("wp-content/uploads")) return url;
  const match = url.match(/\/([^/]+?)\.(?:png|jpg|jpeg|gif)$/i);
  if (!match) return url;
  let stem = match[1].replace(/-(?:scaled|e\d+|\d+x\d+)$/, "");
  const localPath = `/img/${stem}.webp`;
  if (existsSync(join(process.cwd(), "public", localPath))) {
    return localPath;
  }
  return url;
}

function deepMerge<T>(base: T, patch: unknown): T {
  if (patch === null || patch === undefined || patch === "") return base;
  if (Array.isArray(base)) return (Array.isArray(patch) && patch.length > 0 ? patch : base) as T;
  if (typeof base === "object" && base !== null) {
    if (typeof patch !== "object" || Array.isArray(patch)) return base;
    const out: Record<string, unknown> = { ...(base as Record<string, unknown>) };
    for (const [k, v] of Object.entries(patch as Record<string, unknown>)) {
      if (k in out) out[k] = deepMerge(out[k], v);
    }
    return out as T;
  }
  return patch as T;
}

/** Walk a content object and convert all WordPress image URLs to local WebP paths. */
function convertContentImages(obj: unknown): void {
  if (!obj || typeof obj !== "object") return;
  for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
    if (typeof value === "string" && value.includes("wp-content/uploads")) {
      (obj as Record<string, unknown>)[key] = wpImageToLocal(value);
    } else if (Array.isArray(value)) {
      for (let i = 0; i < value.length; i++) {
        if (typeof value[i] === "string" && value[i].includes("wp-content/uploads")) {
          value[i] = wpImageToLocal(value[i]);
        } else {
          convertContentImages(value[i]);
        }
      }
    } else if (typeof value === "object" && value !== null) {
      convertContentImages(value);
    }
  }
}

const homePageId = process.env.WORDPRESS_HOME_ID || "180";

interface GraphQLSection {
  type: string;
  title?: string | null;
  image?: string | null;
  content?: string | null;
  buttonText?: string | null;
  buttonUrl?: string | null;
  stats?: Array<{ number?: string | null; label?: string | null }> | null;
  eyebrow?: string | null;
  heading?: string | null;
  heading_highlight?: string | null;
  partners?: Array<{ name?: string | null; url?: string | null; logo?: string | null }> | null;
  features?: Array<{ heading?: string | null; description?: string | null; iconSvg?: string | null }> | null;
  personas?: Array<{ index?: string | null; heading?: string | null; description?: string | null }> | null;
  testimonials?: Array<{ quote?: string | null; initials?: string | null; name?: string | null; role?: string | null }> | null;
  faqs?: Array<{ question?: string | null; answer?: string | null }> | null;
  text?: string | null;
  textarea?: string | null;
  description?: string | null;
  logo?: string | null;
  address?: string | null;
  phone?: string | null;
  phone_url?: string | null;
  copyright?: string | null;
  socials?: Array<{ label?: string | null; url?: string | null; iconSvg?: string | null }> | null;
  col1_heading?: string | null;
  col1_links?: Array<{ label?: string | null; url?: string | null }> | null;
  col2_heading?: string | null;
  col2_links?: Array<{ label?: string | null; url?: string | null }> | null;
  col3_heading?: string | null;
  col3_links?: Array<{ label?: string | null; url?: string | null }> | null;
  legal_links?: Array<{ label?: string | null; url?: string | null }> | null;
  products?: Array<{
    code?: string | null; tagline?: string | null; price?: string | null;
    cadence?: string | null; strength?: string | null; best?: string | null;
    accent?: string | null; image?: string | null; features?: string[] | null;
  }> | null;
  serif_line?: string | null;
  subheading?: string | null;
  promoCode?: string | null;
  discount_prefix?: string | null;
  discount_value?: string | null;
  discount_suffix?: string | null;
  badge?: string | null;
  title_tail?: string | null;
  form_title?: string | null;
  form_subtitle?: string | null;
  submit_label?: string | null;
  sms_consent?: string | null;
  benefits?: Array<{ text?: string | null }> | null;
  cta_text?: string | null;
  cta_url?: string | null;
  nav_links?: Array<{ label?: string | null; url?: string | null }> | null;
  chip_1_title?: string | null;
  chip_1_subtitle?: string | null;
  chip_2_title?: string | null;
  chip_2_subtitle?: string | null;
  button1Text?: string | null;
  button1Url?: string | null;
  button2Text?: string | null;
  button2Url?: string | null;
  cardLogo?: string | null;
  checklistItems?: Array<{ text?: string | null }> | null;
  statItems?: Array<{ heading?: string | null; text?: string | null }> | null;
  partnerItems?: Array<{ name?: string | null; description?: string | null; logo?: string | null; url?: string | null }> | null;
  cta_heading?: string | null;
  cta_description?: string | null;
  cta_phone?: string | null;
  cta_phone_url?: string | null;
}

function sectionsToHomeContent(sections: GraphQLSection[]): HomeContent {
  const content = JSON.parse(JSON.stringify(defaultContent)) as HomeContent;

  for (const sec of sections) {
    switch (sec.type) {
      case "hero": {
        const heroHeading = sec.heading || sec.title || "";
        content.hero = {
          ...content.hero,
          titleLead: heroHeading ? heroHeading + " " : content.hero.titleLead,
          titleAccent: sec.heading_highlight || content.hero.titleAccent,
          body: sec.content || content.hero.body,
          image: sec.image || content.hero.image,
        badge: sec.badge || content.hero.badge,
        cta: sec.buttonText || content.hero.cta,
        ctaHref: sec.buttonUrl ? rewriteUrl(sec.buttonUrl) : content.hero.ctaHref,
        phoneDisplay: sec.phone || content.hero.phoneDisplay,
          phoneHref: sec.phone_url || content.hero.phoneHref,
          chip1Title: sec.chip_1_title || content.hero.chip1Title,
          chip1Subtitle: sec.chip_1_subtitle || content.hero.chip1Subtitle,
          chip2Title: sec.chip_2_title || content.hero.chip2Title,
          chip2Subtitle: sec.chip_2_subtitle || content.hero.chip2Subtitle,
        };
        break;
      }
      case "stats_bar": {
        if (sec.stats && sec.stats.length > 0) {
          content.stats = sec.stats.map((s) => ({
            n: s.number || "",
            l: s.label || "",
          }));
        }
        break;
      }
      case "partners": {
        if (sec.heading) content.bank.titleLead = sec.heading + " ";
        if (sec.heading_highlight) content.bank.titleAccent = sec.heading_highlight;
        if (sec.eyebrow) content.bank.eyebrow = sec.eyebrow;
        if (sec.partners && sec.partners.length > 0) {
          content.bank.partners = sec.partners.map((p) => ({
            name: p.name || "",
            logo: p.logo || "",
          }));
        }
        break;
      }
      case "features": {
        if (sec.heading) content.why.titleLead = sec.heading + " ";
        if (sec.heading_highlight) content.why.titleAccent = sec.heading_highlight;
        if (sec.eyebrow) content.why.eyebrow = sec.eyebrow;
        if (sec.features && sec.features.length > 0) {
          const iconMap = ["dollar", "phone", "card"] as const;
          content.why.features = sec.features.map((f, i) => ({
            h: f.heading || "",
            p: f.description || "",
            icon: iconMap[i % iconMap.length],
          }));
        }
        break;
      }
      case "testimonials": {
        if (sec.heading) content.reviews.titleLead = sec.heading + " ";
        if (sec.heading_highlight) content.reviews.titleAccent = sec.heading_highlight;
        if (sec.eyebrow) content.reviews.eyebrow = sec.eyebrow;
        if (sec.testimonials && sec.testimonials.length > 0) {
          content.reviews.items = sec.testimonials.map((t) => ({
            quote: t.quote || "",
            initials: t.initials || "",
            name: t.name || "",
            role: t.role || "",
          }));
        }
        break;
      }
      case "faq": {
        if (sec.heading) content.faq.titleLead = sec.heading + " ";
        if (sec.heading_highlight) content.faq.titleAccent = sec.heading_highlight;
        if (sec.eyebrow) content.faq.eyebrow = sec.eyebrow;
        if (sec.faqs && sec.faqs.length > 0) {
          content.faq.items = sec.faqs.map((f) => ({
            q: f.question || "",
            a: f.answer || "",
          }));
        }
        break;
      }
      case "cta_banner": {
        content.finalCta = {
          ...content.finalCta,
          titleLead: (sec.heading ? sec.heading + " " : content.finalCta.titleLead),
          titleAccent: sec.heading_highlight || content.finalCta.titleAccent,
          titleTail: sec.title_tail || content.finalCta.titleTail,
          body: sec.description || content.finalCta.body,
        };
        if (sec.buttonText) content.finalCta.cta = sec.buttonText;
        if (sec.buttonUrl) content.finalCta.ctaUrl = rewriteUrl(sec.buttonUrl);
        break;
      }
      case "footer": {
        if (sec.description) content.footer.blurb = sec.description;
        if (sec.address) content.footer.address = sec.address;
        if (sec.phone) content.footer.phone = sec.phone;
        if (sec.logo) content.footer.logo = sec.logo;
        if (sec.col1_heading) content.footer.col1Heading = sec.col1_heading;
        if (sec.col2_heading) content.footer.col2Heading = sec.col2_heading;
        if (sec.col3_heading) content.footer.col3Heading = sec.col3_heading;
        if (sec.col1_links && sec.col1_links.length > 0) content.footer.col1Links = sec.col1_links.map(l => ({ label: l.label || "", url: l.url || "" }));
        if (sec.col2_links && sec.col2_links.length > 0) content.footer.col2Links = sec.col2_links.map(l => ({ label: l.label || "", url: l.url || "" }));
        if (sec.col3_links && sec.col3_links.length > 0) content.footer.col3Links = sec.col3_links.map(l => ({ label: l.label || "", url: l.url || "" }));
        if (sec.legal_links && sec.legal_links.length > 0) content.footer.legalLinks = sec.legal_links.map(l => ({ label: l.label || "", url: l.url || "" }));
        break;
      }
      case "pricing": {
        if (sec.heading) content.packages.titleLead = sec.heading + " ";
        if (sec.heading_highlight) content.packages.titleAccent = sec.heading_highlight;
        if (sec.eyebrow) content.packages.eyebrow = sec.eyebrow;
        if (sec.description) content.packages.body = sec.description;
        if (sec.buttonUrl) content.packages.ctaUrl = rewriteUrl(sec.buttonUrl);
        if (sec.products && sec.products.length > 0) {
          content.packages.products = sec.products.map((p) => ({
            code: p.code || "",
            img: p.image || "/img/box-tw.webp",
            tagline: p.tagline || "",
            price: p.price || "",
            cadence: p.cadence || "",
            features: p.features || [],
          }));
        }
        break;
      }
      case "header": {
        content.header = {
          ...content.header,
          logo: sec.logo || content.header.logo,
          ctaText: sec.cta_text || content.header.ctaText,
          ctaUrl: sec.cta_url ? rewriteUrl(sec.cta_url) : content.header.ctaUrl,
          navLinks: sec.nav_links && sec.nav_links.length > 0
            ? sec.nav_links.map((l) => ({ label: l.label || "", url: l.url || "" }))
            : content.header.navLinks,
        };
        break;
      }
      case "promo_banner": {
        if (sec.eyebrow) content.promo.eyebrow = sec.eyebrow;
        if (sec.serif_line) content.promo.scriptLine = sec.serif_line;
        if (sec.heading) content.promo.headline = sec.heading;
        if (sec.subheading) content.promo.subline = sec.subheading;
        if (sec.description) content.promo.body = sec.description;
        if (sec.promoCode) content.promo.code = sec.promoCode;
        if (sec.buttonText) content.promo.cta = sec.buttonText;
        if (sec.buttonUrl) content.promo.ctaUrl = rewriteUrl(sec.buttonUrl);
        if (sec.discount_prefix) content.promo.discountPrefix = sec.discount_prefix;
        if (sec.discount_value) content.promo.discountValue = sec.discount_value;
        if (sec.discount_suffix) content.promo.discountSuffix = sec.discount_suffix;
        if (sec.image) content.promo.image = sec.image;
        break;
      }
      case "lead": {
        if (sec.badge) content.lead.badge = sec.badge;
        if (sec.heading) content.lead.titleLead = sec.heading + " ";
        if (sec.heading_highlight) content.lead.titleAccent = sec.heading_highlight;
        if (sec.description) content.lead.body = sec.description;
        if (sec.form_title) content.lead.formTitle = sec.form_title;
        if (sec.form_subtitle) content.lead.formSubtitle = sec.form_subtitle;
        if (sec.submit_label) content.lead.submitLabel = sec.submit_label;
        if (sec.sms_consent) content.lead.smsConsent = sec.sms_consent;
        if (sec.phone) content.lead.phoneDisplay = sec.phone;
        if (sec.phone_url) content.lead.phoneHref = sec.phone_url;
        if (sec.benefits && sec.benefits.length > 0) {
          content.lead.benefits = sec.benefits.map((b) => b.text || "");
        }
        break;
      }
    }
  }

  return content;
}

const HOME_QUERY = `
  query HomeData($id: ID!, $idType: PageIdType) {
    generalSettings {
      title
      description
      siteIcon { node { sourceUrl } }
    }
    page(id: $id, idType: $idType) {
      dynamicSections {
        type
        title
        image
        content
        buttonText
        buttonUrl
        stats { number label }
        eyebrow
        heading
        heading_highlight
        partners { name url logo }
        features { heading description iconSvg }
        personas { index heading description }
        steps { index heading description }
        testimonials { quote initials name role }
        faqs { question answer }
        description
        text
        textarea
        rows { featureName tw sr dr }
        logo
        address
        phone
        phone_url
        copyright
        socials { label url iconSvg }
        col1_heading
        col1_links { label url }
        col2_heading
        col2_links { label url }
        col3_heading
        col3_links { label url }
        legal_links { label url }
        products { code tagline price cadence strength best accent image features }
        serif_line
        subheading
        promoCode
        badge
        title_tail
        discount_prefix
        discount_value
        discount_suffix
        form_title
        form_subtitle
        submit_label
        sms_consent
        benefits { text }
        cta_text
        cta_url
        nav_links { label url }
        chip_1_title
        chip_1_subtitle
        chip_2_title
        chip_2_subtitle
        button1Text
        button1Url
        button2Text
        button2Url
        cardLogo
        checklistItems { text }
        infoBoxHeading
        infoBoxContent
        crossLinkText
        crossLinkUrl
        statItems { heading text }
        partnerItems { name description logo url }
        cta_heading
        cta_description
        cta_phone
        cta_phone_url
        members { name role description image url }
        toll_free
        fax
        support_email
        sales_email
        season_hours
        embed_url
        map_label
        address_line1
        address_line2
        directions_url
        gallery { image }
      }
    }
  }
`;

export async function getHomeContent(): Promise<HomeContent> {
  if (!GRAPHQL_URL || !WP_URL) return defaultContent;

  try {
    const [graphRes, seoRes] = await Promise.all([
      fetch(GRAPHQL_URL, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          query: HOME_QUERY,
          variables: { id: homePageId, idType: "DATABASE_ID" },
        }),
        next: { revalidate: REVALIDATE_SECONDS, tags: ["home"] },
      }),
      fetch(`${WP_URL}/wp-json/wp/v2/pages/${homePageId}?_fields=aioseo_head_json`, {
        next: { revalidate: REVALIDATE_SECONDS, tags: ["home"] },
      }),
    ]);

    const content = JSON.parse(JSON.stringify(defaultContent)) as HomeContent;

    if (graphRes.ok) {
      const json = await graphRes.json();
      const sections: GraphQLSection[] | undefined = json?.data?.page?.dynamicSections;
      if (Array.isArray(sections) && sections.length > 0) {
        Object.assign(content, sectionsToHomeContent(sections));
      }

      const gs = json?.data?.generalSettings;
      if (gs) {
        if (gs.title) content.siteTitle = gs.title;
        if (gs.description) content.siteDescription = gs.description;
        if (gs.siteIcon?.node?.sourceUrl) content.siteIcon = gs.siteIcon.node.sourceUrl;
      }
    }

    if (seoRes.ok) {
      const seoJson = await seoRes.json();
      const seo = seoJson?.aioseo_head_json;
      if (seo) {
        content.seo = {
          title: seo.title || content.seo.title,
          description: seo.description || content.seo.description,
          canonicalUrl: seo.canonical_url ? rewriteUrl(seo.canonical_url) : content.seo.canonicalUrl,
          robots: seo.robots || content.seo.robots,
          keywords: seo.keywords || content.seo.keywords,
          ogTitle: seo["og:title"] || seo.title || content.seo.ogTitle,
          ogDescription: seo["og:description"] || seo.description || content.seo.ogDescription,
          ogType: seo["og:type"] || content.seo.ogType,
          ogUrl: seo["og:url"] ? rewriteUrl(seo["og:url"]) : content.seo.ogUrl,
          ogSiteName: seo["og:site_name"] || content.seo.ogSiteName,
          twitterTitle: seo["twitter:title"] || seo.title || content.seo.twitterTitle,
          twitterDescription: seo["twitter:description"] || seo.description || content.seo.twitterDescription,
          twitterCard: seo["twitter:card"] || content.seo.twitterCard,
        };
      }
    }

    // Convert any remaining WP image URLs to local WebP paths
    convertContentImages(content);

    // If AIOSEO description is truncated (no sentence-ending punctuation), use siteDescription instead
    const desc = content.seo.description;
    if (desc && !/[.!?]$/.test(desc) && content.siteDescription) {
      content.seo.description = content.siteDescription;
      content.seo.ogDescription = content.siteDescription;
      content.seo.twitterDescription = content.siteDescription;
    }

    return content;
  } catch {
    return defaultContent;
  }
}

export interface WpPost {
  id: number;
  slug: string;
  title: string;
  excerpt: string;
  date: string;
  link: string;
  image: string | null;
}

export interface WpPostFull extends WpPost {
  content: string;
}

const namedEntities: Record<string, string> = {
  amp: "&", lt: "<", gt: ">", quot: '"', apos: "'",
  nbsp: " ", hellip: "…", ndash: "–", mdash: "—",
  lsquo: "'", rsquo: "'", ldquo: "“", rdquo: "”",
};

function stripTags(html: string): string {
  return html
    .replace(/<[^>]*>/g, "")
    .replace(/&#(\d+);/g, (_, n) => String.fromCodePoint(Number(n)))
    .replace(/&#x([0-9a-f]+);/gi, (_, n) => String.fromCodePoint(parseInt(n, 16)))
    .replace(/&([a-z]+);/gi, (m, name: string) => namedEntities[name.toLowerCase()] ?? m)
    .trim();
}

export interface PostsResult { posts: WpPost[]; totalPages: number }

export async function getPosts(perPage = 9, page = 1): Promise<PostsResult> {
  const url = WP_URL ? `${WP_URL}/wp-json/wp/v2/posts?per_page=${perPage}&page=${page}&_embed=wp:featuredmedia&_fields=id,slug,title,excerpt,date,link,_links,_embedded` : null;
  if (!url) return { posts: [], totalPages: 0 };
  try {
    const res = await fetch(url, { next: { revalidate: REVALIDATE_SECONDS, tags: ["posts"] } });
    if (!res.ok) return { posts: [], totalPages: 0 };
    const totalPages = Number(res.headers.get("X-WP-TotalPages") || "1");
    const raw = (await res.json()) as Array<{
      id: number; slug: string; date: string; link: string;
      title?: { rendered?: string }; excerpt?: { rendered?: string };
      _embedded?: { "wp:featuredmedia"?: Array<{ source_url?: string }> };
    }>;
    const posts = raw.map((p) => ({
      id: p.id, slug: p.slug,
      title: stripTags(p.title?.rendered ?? ""),
      excerpt: stripTags(p.excerpt?.rendered ?? ""),
      date: p.date, link: p.link,
      image: p._embedded?.["wp:featuredmedia"]?.[0]?.source_url ?? null,
    }));
    return { posts, totalPages };
  } catch { return { posts: [], totalPages: 0 }; }
}

export async function getPost(slug: string): Promise<WpPostFull | null> {
  const url = WP_URL ? `${WP_URL}/wp-json/wp/v2/posts?slug=${encodeURIComponent(slug)}&_embed=wp:featuredmedia&_fields=id,slug,title,excerpt,content,date,link,_links,_embedded` : null;
  if (!url) return null;
  try {
    const res = await fetch(url, { next: { revalidate: REVALIDATE_SECONDS, tags: [`post:${slug}`] } });
    if (!res.ok) return null;
    const raw = (await res.json()) as Array<{
      id: number; slug: string; date: string; link: string;
      title?: { rendered?: string }; excerpt?: { rendered?: string }; content?: { rendered?: string };
      _embedded?: { "wp:featuredmedia"?: Array<{ source_url?: string }> };
    }>;
    if (!raw.length) return null;
    const p = raw[0];
    return {
      id: p.id, slug: p.slug,
      title: stripTags(p.title?.rendered ?? ""),
      excerpt: stripTags(p.excerpt?.rendered ?? ""),
      content: p.content?.rendered ?? "",
      date: p.date, link: p.link,
      image: p._embedded?.["wp:featuredmedia"]?.[0]?.source_url ?? null,
    };
  } catch { return null; }
}

/* ------------------------------------------------------------------ */
/*  Page content (any WordPress page via pageBy)                       */
/* ------------------------------------------------------------------ */

const PAGE_QUERY = `
  query PageData($uri: String!) {
    pageBy(uri: $uri) {
      databaseId
      title
      uri
      content
      dynamicSections {
        type
        title
        image
        content
        buttonText
        buttonUrl
        stats { number label }
        eyebrow
        heading
        heading_highlight
        partners { name url logo }
        features { heading description iconSvg }
        personas { index heading description }
        steps { index heading description }
        testimonials { quote initials name role }
        faqs { question answer }
        description
        text
        textarea
        rows { featureName tw sr dr }
        logo
        address
        phone
        phone_url
        copyright
        socials { label url iconSvg }
        col1_heading
        col1_links { label url }
        col2_heading
        col2_links { label url }
        col3_heading
        col3_links { label url }
        legal_links { label url }
        products { code tagline price cadence strength best accent image features }
        serif_line
        subheading
        promoCode
        badge
        title_tail
        discount_prefix
        discount_value
        discount_suffix
        form_title
        form_subtitle
        submit_label
        sms_consent
        benefits { text }
        cta_text
        cta_url
        nav_links { label url }
        chip_1_title
        chip_1_subtitle
        chip_2_title
        chip_2_subtitle
        button1Text
        button1Url
        button2Text
        button2Url
        cardLogo
        checklistItems { text }
        infoBoxHeading
        infoBoxContent
        crossLinkText
        crossLinkUrl
        statItems { heading text }
        partnerItems { name description logo url }
        cta_heading
        cta_description
        cta_phone
        cta_phone_url
        members { name role description image url }
        toll_free
        fax
        support_email
        sales_email
        season_hours
        embed_url
        map_label
        address_line1
        address_line2
        directions_url
        gallery { image }
      }
    }
  }
`;

interface RawMenuItem {
  id: string;
  parentId: string | null;
  label: string;
  uri: string;
  path: string;
  url: string;
  target: string;
}

interface MenuQueryResult {
  data?: {
    menu?: {
      menuItems?: {
        nodes?: RawMenuItem[];
      };
    };
  };
}

const MENU_QUERY = `
  {
    menu(id: "Main", idType: NAME) {
      menuItems(first: 100) {
        nodes {
          id
          parentId
          label
          uri
          path
          url
          target
        }
      }
    }
  }
`;

const ALL_PAGES_QUERY = `
  {
    pages(first: 200) {
      nodes {
        uri
      }
    }
  }
`;

/**
 * Fetch all known page URIs from WordPress for SSG path generation.
 * Uses WPGraphQL which returns correct nested URI paths (e.g.
 * "/services/sigma-1040-tw/") without needing to reconstruct from
 * parent relationships.
 */
export async function getAllPageUris(): Promise<string[]> {
  if (!GRAPHQL_URL) return [];
  try {
    const res = await fetch(GRAPHQL_URL, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ query: ALL_PAGES_QUERY }),
      next: { revalidate: REVALIDATE_SECONDS, tags: ["pages"] },
    });
    if (!res.ok) return [];
    const json = await res.json();
    const nodes: Array<{ uri: string }> | undefined = json?.data?.pages?.nodes;
    if (!nodes) return [];
    return nodes
      .map((n) => n.uri)
      .filter((u) => u && u !== "/")
      .sort();
  } catch {
    return [];
  }
}

/* Batch cache for all pages' AIOSEO SEO data, keyed by WordPress databaseId. */
let seoBatchCache: Map<number, Record<string, string>> | null = null;
let seoBatchPromise: Promise<Map<number, Record<string, string>>> | null = null;

/**
 * Fetch AIOSEO data for ALL published pages in a single batch REST request.
 * This avoids N+1 — one request instead of one per page during build.
 * Pagination is handled via X-WP-TotalPages header.
 *
 * Acceptability: during `next build` (static export), each page already makes
 * one WPGraphQL call. Adding a single batch REST call (or two for >100 pages)
 * is negligible. An N+1 pattern (one REST call per page) would instead multiply
 * requests by page count (~65 pages), which is wasteful. WPGraphQL would be
 * ideal but the AIOSEO fields are not exposed in the GraphQL schema — only
 * via the REST `aioseo_head_json` field used here.
 */
async function getSeoBatch(): Promise<Map<number, Record<string, string>>> {
  if (seoBatchCache) return seoBatchCache;
  if (seoBatchPromise) return seoBatchPromise;

  seoBatchPromise = (async () => {
    const map = new Map<number, Record<string, string>>();
    let page = 1;
    let totalPages = 1;

    while (page <= totalPages) {
      const res = await fetch(
        `${WP_URL}/wp-json/wp/v2/pages?_fields=id,slug,link,title,aioseo_head_json&per_page=100&page=${page}`,
        { next: { revalidate: REVALIDATE_SECONDS, tags: ["pages"] } },
      );
      if (!res.ok) break;

      const totalHeader = res.headers.get("X-WP-TotalPages");
      if (totalHeader) totalPages = parseInt(totalHeader, 10);

      const pages: Array<{
        id: number;
        aioseo_head_json: Record<string, string> | null;
      }> = await res.json();

      for (const p of pages) {
        if (p.aioseo_head_json) {
          map.set(p.id, p.aioseo_head_json);
        }
      }
      page++;
    }

    seoBatchCache = map;
    return map;
  })();

  return seoBatchPromise;
}

/**
 * Fetch a WordPress page by its URI path (e.g. "/bank-products/eps-financial/").
 * Returns null when the page is not found or WP is unreachable.
 *
 * SEO metadata is populated from the batch AIOSEO cache (see getSeoBatch).
 */
export async function getPageContent(uri: string): Promise<PageContent | null> {
  if (!GRAPHQL_URL || !WP_URL) return null;
  try {
    const [graphRes, seoMap] = await Promise.all([
      fetch(GRAPHQL_URL, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ query: PAGE_QUERY, variables: { uri } }),
        next: { revalidate: REVALIDATE_SECONDS, tags: [`page:${uri}`] },
      }),
      getSeoBatch(),
    ]);
    if (!graphRes.ok) return null;
    const json = await graphRes.json();
    const page = json?.data?.pageBy;
    if (!page) return null;

    const id: number | undefined = page.databaseId;
    const seoRaw = id ? seoMap.get(id) : undefined;

    const result: PageContent = {
      title: page.title ?? "",
      uri: page.uri ?? uri,
      bodyHtml: page.content ?? "",
      sections: page.dynamicSections ?? [],
      seo: {
        title: seoRaw?.title ?? "",
        description: seoRaw?.description ?? "",
        canonical: seoRaw?.canonical_url ? rewriteUrl(seoRaw.canonical_url) : "",
        robots: seoRaw?.robots ?? "",
        keywords: seoRaw?.keywords ?? "",
        ogTitle: seoRaw?.["og:title"] ?? seoRaw?.title ?? "",
        ogDescription: seoRaw?.["og:description"] ?? seoRaw?.description ?? "",
        ogType: seoRaw?.["og:type"] ?? "website",
        ogUrl: seoRaw?.["og:url"] ? rewriteUrl(seoRaw["og:url"]) : "",
        ogSiteName: seoRaw?.["og:site_name"] ?? "",
        twitterTitle: seoRaw?.["twitter:title"] ?? seoRaw?.title ?? "",
        twitterDescription: seoRaw?.["twitter:description"] ?? seoRaw?.description ?? "",
        twitterCard: seoRaw?.["twitter:card"] ?? "summary_large_image",
      },
    };

    // If AIOSEO description is truncated (no sentence-ending punctuation), clear it
    const desc = result.seo.description;
    if (desc && !/[.!?]$/.test(desc)) {
      result.seo.description = "";
      result.seo.ogDescription = "";
      result.seo.twitterDescription = "";
    }

    return result;
  } catch {
    return null;
  }
}

/**
 * Build a nested menu tree from the WordPress PRIMARY location menu.
 */
function buildTree(items: RawMenuItem[]): MenuItem[] {
  const map = new Map<string, MenuItem>();
  const roots: MenuItem[] = [];

  for (const n of items) {
    const href = n.uri && n.uri !== "#" ? n.uri : n.url || n.path || "#";
    map.set(n.id, {
      id: n.id,
      label: n.label,
      url: href,
      uri: href,
      target: n.target || "",
      children: [],
    });
  }

  for (const n of items) {
    const node = map.get(n.id)!;
    if (n.parentId && map.has(n.parentId)) {
      map.get(n.parentId)!.children.push(node);
    } else {
      roots.push(node);
    }
  }

  return roots;
}

/**
 * Fetch the Main navigation menu from WordPress.
 */
export async function getMenu(): Promise<MenuItem[]> {
  if (!GRAPHQL_URL) return [];
  try {
    const res = await fetch(GRAPHQL_URL, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ query: MENU_QUERY }),
      next: { revalidate: REVALIDATE_SECONDS, tags: ["menu"] },
    });
    if (!res.ok) return [];
    const json: MenuQueryResult = await res.json();
    const nodes = json?.data?.menu?.menuItems?.nodes;
    if (!nodes) return [];
    return buildTree(nodes);
  } catch {
    return [];
  }
}

const ALL_PAGES_TITLES_QUERY = `
  {
    pages(first: 200) {
      nodes {
        uri
        title
      }
    }
  }
`;

export async function getBreadcrumbsFromUri(uri: string): Promise<BreadcrumbItem[]> {
  if (!GRAPHQL_URL) return [];
  try {
    const res = await fetch(GRAPHQL_URL, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ query: ALL_PAGES_TITLES_QUERY }),
      next: { revalidate: REVALIDATE_SECONDS, tags: ["pages"] },
    });
    if (!res.ok) return [];
    const json = await res.json();
    const nodes: Array<{ uri: string; title: string }> | undefined = json?.data?.pages?.nodes;
    if (!nodes) return [];

    const uriMap = new Map<string, string>();
    for (const n of nodes) {
      uriMap.set(n.uri, n.title);
    }

    const segments = uri.split("/").filter(Boolean);
    const crumbs: BreadcrumbItem[] = [{ label: "Home", href: "/" }];
    let path = "";
    for (let i = 0; i < segments.length; i++) {
      path += "/" + segments[i] + "/";
      const title = uriMap.get(path);
      const label = title || segments[i].replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
      const isLast = i === segments.length - 1;
      crumbs.push(isLast ? { label } : { label, href: path });
    }
    return crumbs;
  } catch {
    return [];
  }
}

export interface LeadFields {
  firstName: string; lastName: string; company: string;
  email: string; phone: string; smsConsent: boolean;
}

export type LeadResult =
  | { ok: true }
  | { ok: false; reason: "not_configured" | "rejected" | "unreachable"; message?: string };

export async function submitLead(fields: LeadFields): Promise<LeadResult> {
  const formId = process.env.WORDPRESS_CF7_FORM_ID;
  const url = formId && WP_URL ? `${WP_URL}/wp-json/contact-form-7/v1/contact-forms/${formId}/feedback` : null;
  if (!url) return { ok: false, reason: "not_configured" };

  const body = new FormData();
  body.set("your-first-name", fields.firstName);
  body.set("your-last-name", fields.lastName);
  body.set("your-company", fields.company);
  body.set("your-email", fields.email);
  body.set("your-phone", fields.phone);
  body.set("sms-consent", fields.smsConsent ? "yes" : "no");
  body.set("_wpcf7_unit_tag", `wpcf7-f${process.env.WORDPRESS_CF7_FORM_ID}-o1`);

  try {
    const res = await fetch(url, { method: "POST", body });
    const json = (await res.json()) as { status?: string; message?: string };
    if (json.status === "mail_sent") return { ok: true };
    return { ok: false, reason: "rejected", message: json.message };
  } catch {
    return { ok: false, reason: "unreachable" };
  }
}
