import prisma from "../db.server";
import { registerShop } from "./shop.server";
import { ShopifyAdminClient } from "../lib/shopify/admin";
import { ShopifyStorefrontClient } from "../lib/shopify/storefront";
import { createNotification } from "./notification.server";

export interface ArticleSyncResult {
  success: boolean;
  syncedCount: number;
  message: string;
}

export interface StoreArticle {
  id: string;
  shopifyArticleId: string;
  blogHandle: string;
  blogTitle?: string | null;
  title: string;
  handle: string;
  summary?: string | null;
  content?: string | null;
  image?: string | null;
  imageUrl?: string | null;
  author?: string | null;
  tags?: any;
  url?: string | null;
  publishedAt?: Date | string | null;
}

/**
 * Normalizes a store URL to ensure it has a full https:// scheme and no trailing slash.
 */
function normalizeStoreBaseUrl(shopDomain: string, primaryDomainUrl?: string | null): string {
  if (primaryDomainUrl && primaryDomainUrl.startsWith("http")) {
    return primaryDomainUrl.replace(/\/+$/, "");
  }
  const clean = (shopDomain || "").trim().toLowerCase();
  if (clean.startsWith("http://") || clean.startsWith("https://")) {
    return clean.replace(/\/+$/, "");
  }
  return `https://${clean}`;
}

/**
 * Removes legacy mock/seed articles and dummy knowledge base entries for a shop.
 */
export async function cleanupLegacyDummyData(shopId: string) {
  try {
    // Delete legacy mock articles
    await prisma.articleCache.deleteMany({
      where: {
        shopId,
        OR: [
          { shopifyArticleId: { startsWith: "gid://shopify/Article/seed-" } },
          { handle: { in: ["ultimate-sizing-fit-measurement-guide", "product-care-and-maintenance-tips", "our-story-sustainable-craftsmanship"] } },
        ],
      },
    });

    // Delete legacy mock blog KB entries
    await prisma.knowledgeBase.deleteMany({
      where: {
        shopId,
        sourceType: "blog",
        OR: [
          { sourceUrl: { contains: "ultimate-sizing-fit" } },
          { sourceUrl: { contains: "product-care-and-maintenance" } },
          { sourceUrl: { contains: "our-story-sustainable" } },
          { sourceUrl: { startsWith: "/blogs/" } },
        ],
      },
    });
  } catch (err) {
    console.warn("[cleanupLegacyDummyData] Warning:", err);
  }
}

/**
 * Syncs real blog articles for a given store from Shopify Admin API into ArticleCache & KnowledgeBase.
 * Strictly no dummy/mock seed articles.
 */
export async function syncStoreArticles(shop: string, articlesData?: Array<any>): Promise<ArticleSyncResult> {
  const normalizedDomain = shop.toLowerCase().trim();
  let store = await prisma.shop.findUnique({ where: { shopifyDomain: normalizedDomain } });

  if (!store) {
    store = await registerShop({
      shopifyDomain: normalizedDomain,
      accessToken: "shpss_sample_token",
      scopes: "read_products,read_content",
    });
  }

  // Clean up any old dummy seed articles first
  await cleanupLegacyDummyData(store.id);

  let fetchedArticles: Array<any> = [];
  let baseUrl = `https://${normalizedDomain}`;

  if (articlesData && Array.isArray(articlesData) && articlesData.length > 0) {
    fetchedArticles = articlesData.map((item: any) => {
      const cleanSummary = (item.summary || item.excerpt || item.excerptHtml || item.body_html || item.content || "")
        .replace(/<[^>]*>?/gm, "")
        .substring(0, 300)
        .trim();

      const cleanContent = (item.contentHtml || item.content || item.body_html || item.summary || "")
        .replace(/<[^>]*>?/gm, "")
        .trim();

      const blogHandle = item.blogHandle || item.blog?.handle || "news";
      const blogTitle = item.blogTitle || item.blog?.title || "Store Blog";
      const handle = item.handle || "";
      const rawUrl = item.url || item.onlineStoreUrl || `/blogs/${blogHandle}/${handle}`;
      const url = rawUrl.startsWith("http") ? rawUrl : `${baseUrl}${rawUrl.startsWith("/") ? "" : "/"}${rawUrl}`;
      const imageSrc = item.image?.url || item.image?.src || item.image || item.imageUrl || "";

      return {
        shopifyArticleId: String(item.id || item.shopifyArticleId || `gid://shopify/Article/${Date.now()}`),
        blogHandle,
        blogTitle,
        title: item.title || "Untitled Article",
        handle,
        summary: cleanSummary,
        content: cleanContent,
        image: imageSrc,
        author: item.authorV2?.name || item.author || "Store Author",
        tags: Array.isArray(item.tags) ? item.tags : (typeof item.tags === "string" ? item.tags.split(",").map((t: string) => t.trim()) : []),
        url,
        publishedAt: item.publishedAt ? new Date(item.publishedAt) : new Date(),
      };
    });
  }

  // Attempt to fetch live articles via Admin GraphQL API if no articles passed
  if (fetchedArticles.length === 0) {
    try {
      const adminClient = new ShopifyAdminClient({ shopifyDomain: shop });
      const res = await adminClient.getStoreBlogsAndArticles();
      
      const primaryDomainUrl = res?.shop?.primaryDomain?.url;
      baseUrl = normalizeStoreBaseUrl(normalizedDomain, primaryDomainUrl);

      const blogEdges = res?.blogs?.edges || [];
      for (const bEdge of blogEdges) {
        const blogNode = bEdge.node;
        const blogHandle = blogNode.handle || "news";
        const blogTitle = blogNode.title || "Store Blog";
        const articleEdges = blogNode.articles?.edges || [];
        
        for (const aEdge of articleEdges) {
          const node = aEdge.node;
          const cleanSummary = (node.summary || node.body || node.excerpt || node.excerptHtml || node.contentHtml || "")
            .replace(/<[^>]*>?/gm, "")
            .substring(0, 300)
            .trim();
          const cleanContent = (node.body || node.summary || node.contentHtml || node.excerptHtml || "")
            .replace(/<[^>]*>?/gm, "")
            .trim();

          const rawUrl = node.onlineStoreUrl || `/blogs/${blogHandle}/${node.handle}`;
          const articleUrl = rawUrl.startsWith("http") ? rawUrl : `${baseUrl}${rawUrl.startsWith("/") ? "" : "/"}${rawUrl}`;

          fetchedArticles.push({
            shopifyArticleId: node.id,
            blogHandle,
            blogTitle,
            title: node.title || "Untitled Article",
            handle: node.handle || "",
            summary: cleanSummary,
            content: cleanContent,
            image: node.image?.url || "",
            author: node.author?.name || node.authorV2?.name || "Store Author",
            tags: node.tags || [],
            url: articleUrl,
            publishedAt: node.publishedAt ? new Date(node.publishedAt) : new Date(),
          });
        }
      }
    } catch (e) {
      console.warn("[syncStoreArticles] GraphQL fetch error for", shop, e);
    }
  }

  // Attempt REST Admin API fallback if GraphQL returned no articles
  if (fetchedArticles.length === 0) {
    try {
      const { getValidAdminAccessToken } = await import("./shop.server");
      const accessToken = await getValidAdminAccessToken(shop);
      if (accessToken) {
        const restRes = await fetch(`https://${normalizedDomain}/admin/api/2025-01/articles.json`, {
          headers: { "X-Shopify-Access-Token": accessToken },
        });
        if (restRes.ok) {
          const restData = await restRes.json();
          const restArticles = restData.articles || [];
          for (const item of restArticles) {
            const cleanSummary = (item.summary_html || item.body_html || "")
              .replace(/<[^>]*>?/gm, "")
              .substring(0, 300)
              .trim();
            const cleanContent = (item.body_html || item.summary_html || "")
              .replace(/<[^>]*>?/gm, "")
              .trim();
            const rawUrl = `/blogs/news/${item.handle}`;
            const articleUrl = `${baseUrl}${rawUrl}`;

            fetchedArticles.push({
              shopifyArticleId: String(item.id),
              blogHandle: "news",
              blogTitle: "News & Articles",
              title: item.title || "Untitled Article",
              handle: item.handle || "",
              summary: cleanSummary,
              content: cleanContent,
              image: item.image?.src || "",
              author: item.author || "Store Author",
              tags: typeof item.tags === "string" ? item.tags.split(",").map((t: string) => t.trim()) : (Array.isArray(item.tags) ? item.tags : []),
              url: articleUrl,
              publishedAt: item.published_at ? new Date(item.published_at) : new Date(),
            });
          }
        }
      }
    } catch (restErr) {
      console.warn("[syncStoreArticles] REST fetch error for", shop, restErr);
    }
  }

  let syncedCount = 0;

  for (const item of fetchedArticles) {
    await prisma.articleCache.upsert({
      where: {
        shopId_shopifyArticleId: {
          shopId: store.id,
          shopifyArticleId: item.shopifyArticleId,
        },
      },
      update: {
        blogHandle: item.blogHandle || "news",
        blogTitle: item.blogTitle || "Store Blog",
        title: item.title,
        handle: item.handle,
        summary: item.summary || "",
        content: item.content || "",
        image: item.image || "",
        author: item.author || "",
        tags: item.tags || [],
        url: item.url,
        publishedAt: item.publishedAt || new Date(),
        updatedAt: new Date(),
      },
      create: {
        shopId: store.id,
        shopifyArticleId: item.shopifyArticleId,
        blogHandle: item.blogHandle || "news",
        blogTitle: item.blogTitle || "Store Blog",
        title: item.title,
        handle: item.handle,
        summary: item.summary || "",
        content: item.content || "",
        image: item.image || "",
        author: item.author || "",
        tags: item.tags || [],
        url: item.url,
        publishedAt: item.publishedAt || new Date(),
      },
    });

    // Dual-index into KnowledgeBase for unified cross-search
    const kbSourceUrl = item.url;
    const existingKb = await prisma.knowledgeBase.findFirst({
      where: {
        shopId: store.id,
        sourceUrl: kbSourceUrl,
      },
    });

    const kbContent = `[Blog: ${item.blogTitle || 'Store Blog'}] ${item.title}\n\n${item.summary ? `Summary: ${item.summary}\n\n` : ''}${item.content || ''}`;

    if (existingKb) {
      await prisma.knowledgeBase.update({
        where: { id: existingKb.id },
        data: {
          title: item.title,
          content: kbContent,
          sourceType: "blog",
        },
      });
    } else {
      await prisma.knowledgeBase.create({
        data: {
          shopId: store.id,
          title: item.title,
          content: kbContent,
          sourceType: "blog",
          sourceUrl: kbSourceUrl,
          enabled: true,
        },
      });
    }

    syncedCount++;
  }

  if (syncedCount > 0) {
    await createNotification({
      shopId: store.id,
      type: "SHOPIFY_SYNC_SUCCESS",
      title: "Store Blog Articles Synced",
      message: `Your store blog posts and articles were successfully synchronized (${syncedCount} articles).`,
      actionUrl: "/app",
    }).catch(() => {});
  }

  return {
    success: true,
    syncedCount,
    message: syncedCount > 0 
      ? `Successfully synchronized ${syncedCount} blog articles for ${shop}.` 
      : `No blog posts found in Shopify for ${shop}.`,
  };
}

/**
 * Syncs store Pages (e.g. Contact Us, About Us, FAQ) and Legal Policies (Privacy, Refund, Shipping, Terms)
 * directly from Shopify Admin API into KnowledgeBase and dynamic chatbot configuration.
 */
export async function syncStorePagesAndPolicies(shop: string): Promise<{ pagesCount: number; policiesCount: number }> {
  const normalizedDomain = shop.toLowerCase().trim();
  let store = await prisma.shop.findUnique({ where: { shopifyDomain: normalizedDomain } });

  if (!store) return { pagesCount: 0, policiesCount: 0 };

  let pagesCount = 0;
  let policiesCount = 0;
  let baseUrl = `https://${normalizedDomain}`;
  let discoveredContactUrl: string | null = null;

  try {
    const adminClient = new ShopifyAdminClient({ shopifyDomain: shop });

    // 1. Fetch Pages via GraphQL
    try {
      const pagesRes = await adminClient.getStorePages();
      const primaryDomainUrl = pagesRes?.shop?.primaryDomain?.url;
      baseUrl = normalizeStoreBaseUrl(normalizedDomain, primaryDomainUrl);

      const pageEdges = pagesRes?.pages?.edges || [];
      for (const edge of pageEdges) {
        const page = edge.node;
        const rawUrl = page.onlineStoreUrl || `/pages/${page.handle}`;
        const pageUrl = rawUrl.startsWith("http") ? rawUrl : `${baseUrl}${rawUrl.startsWith("/") ? "" : "/"}${rawUrl}`;
        const cleanContent = (page.body || page.bodySummary || "")
          .replace(/<[^>]*>?/gm, "")
          .trim();

        // Check if this is the store's contact page
        if (page.handle === "contact" || page.handle === "contact-us" || page.title.toLowerCase().includes("contact")) {
          discoveredContactUrl = pageUrl;
        }

        const existingPageKb = await prisma.knowledgeBase.findFirst({
          where: {
            shopId: store.id,
            sourceUrl: pageUrl,
          },
        });

        const kbContent = `[Page: ${page.title}]\nURL: ${pageUrl}\n\n${cleanContent}`;

        if (existingPageKb) {
          await prisma.knowledgeBase.update({
            where: { id: existingPageKb.id },
            data: {
              title: page.title,
              content: kbContent,
              sourceType: "page",
            },
          });
        } else {
          await prisma.knowledgeBase.create({
            data: {
              shopId: store.id,
              title: page.title,
              content: kbContent,
              sourceType: "page",
              sourceUrl: pageUrl,
              enabled: true,
            },
          });
        }
        pagesCount++;
      }
    } catch (pageErr) {
      console.warn("[syncStorePagesAndPolicies] Pages GraphQL fetch warning:", pageErr);
    }

    // 2. Fetch Shop Details via Admin GraphQL (Zero extra scopes required, zero ACCESS_DENIED)
    try {
      const shopRes = await adminClient.getShopDetails();
      const shopData = shopRes?.shop;
      if (shopData?.primaryDomain?.url) {
        baseUrl = normalizeStoreBaseUrl(normalizedDomain, shopData.primaryDomain.url);
      }
    } catch (shopErr) {
      console.warn("[syncStorePagesAndPolicies] Shop details fetch warning:", shopErr);
    }

    // 3. Fetch Legal Policies via Storefront API (Zero merchant admin scopes required)
    try {
      const storefrontClient = new ShopifyStorefrontClient({ shopifyDomain: shop });
      const sfRes = await storefrontClient.getStorePolicies().catch(() => null);
      const sfShop = sfRes?.shop;

      const policies = [
        { type: "privacy_policy", policy: sfShop?.privacyPolicy, fallbackHandle: "privacy-policy" },
        { type: "refund_policy", policy: sfShop?.refundPolicy, fallbackHandle: "refund-policy" },
        { type: "shipping_policy", policy: sfShop?.shippingPolicy, fallbackHandle: "shipping-policy" },
        { type: "terms_of_service", policy: sfShop?.termsOfService, fallbackHandle: "terms-of-service" },
        { type: "subscription_policy", policy: sfShop?.subscriptionPolicy, fallbackHandle: "subscription-policy" },
      ];

      for (const item of policies) {
        if (item.policy && (item.policy.body || item.policy.url)) {
          const rawUrl = item.policy.url || `/policies/${item.fallbackHandle}`;
          const policyUrl = rawUrl.startsWith("http") ? rawUrl : `${baseUrl}${rawUrl.startsWith("/") ? "" : "/"}${rawUrl}`;
          const cleanContent = (item.policy.body || "")
            .replace(/<[^>]*>?/gm, "")
            .trim();

          const existingPolicyKb = await prisma.knowledgeBase.findFirst({
            where: {
              shopId: store.id,
              sourceUrl: policyUrl,
            },
          });

          const policyTitle = item.policy.title || item.type.replace(/_/g, " ").replace(/\b\w/g, (c: string) => c.toUpperCase());
          const kbContent = `[Policy: ${policyTitle}]\nURL: ${policyUrl}\n\n${cleanContent}`;

          if (existingPolicyKb) {
            await prisma.knowledgeBase.update({
              where: { id: existingPolicyKb.id },
              data: {
                title: policyTitle,
                content: kbContent,
                sourceType: "policy",
              },
            });
          } else {
            await prisma.knowledgeBase.create({
              data: {
                shopId: store.id,
                title: policyTitle,
                content: kbContent,
                sourceType: "policy",
                sourceUrl: policyUrl,
                enabled: true,
              },
            });
          }
          policiesCount++;
        }
      }
    } catch (policyErr) {
      console.warn("[syncStorePagesAndPolicies] Policies Storefront fetch warning:", policyErr);
    }
  } catch (err) {
    console.warn("[syncStorePagesAndPolicies] General error for", shop, err);
  }

  // If a contact page was discovered, update ChatbotSettings widgetTexts.storeContactUrl if not already set or default
  if (discoveredContactUrl) {
    try {
      const settings = await prisma.chatbotSettings.findUnique({ where: { shopId: store.id } });
      if (settings?.systemPrompt) {
        let parsed: any = {};
        try {
          parsed = JSON.parse(settings.systemPrompt);
        } catch {
          parsed = { customPrompt: settings.systemPrompt };
        }
        if (!parsed.storeContactUrl || parsed.storeContactUrl === "/pages/contact") {
          parsed.storeContactUrl = discoveredContactUrl;
          await prisma.chatbotSettings.update({
            where: { shopId: store.id },
            data: { systemPrompt: JSON.stringify(parsed) },
          });
        }
      }
    } catch (cfgErr) {}
  }

  return { pagesCount, policiesCount };
}

/**
 * Searches stored blog articles by keyword across title, summary, content, and tags.
 */
export async function searchArticlesService(
  shopDomain: string,
  query: string,
  limit: number = 5
): Promise<StoreArticle[]> {
  const normalizedDomain = shopDomain.toLowerCase().trim();
  const shop = await prisma.shop.findUnique({ where: { shopifyDomain: normalizedDomain } });
  if (!shop) return [];

  const cleanQuery = (query || "").trim().toLowerCase();
  if (!cleanQuery) {
    const defaultArticles = await prisma.articleCache.findMany({
      where: { shopId: shop.id },
      orderBy: { publishedAt: "desc" },
      take: limit,
    });
    return defaultArticles.map(formatArticle);
  }

  const terms = cleanQuery
    .replace(/[^\w\s]/g, " ")
    .split(/\s+/)
    .filter((w) => w.length >= 2 && !["what", "when", "where", "which", "your", "this", "that", "have", "with", "from", "does", "will", "would", "could", "should", "the", "and", "for"].includes(w));

  const orConditions: any[] = [
    { title: { contains: cleanQuery } },
    { summary: { contains: cleanQuery } },
    { content: { contains: cleanQuery } },
    { blogTitle: { contains: cleanQuery } },
    { blogHandle: { contains: cleanQuery } },
  ];

  for (const term of terms) {
    orConditions.push({ title: { contains: term } });
    orConditions.push({ summary: { contains: term } });
    orConditions.push({ content: { contains: term } });
    orConditions.push({ blogTitle: { contains: term } });
    orConditions.push({ blogHandle: { contains: term } });
  }

  let matches = await prisma.articleCache.findMany({
    where: {
      shopId: shop.id,
      OR: orConditions,
    },
    take: limit * 3,
  });

  // If no direct matches or if the query is asking broadly for news/blogs/guides/articles/latest
  const isGenericBlogQuery = ["news", "blog", "blogs", "article", "articles", "post", "posts", "guide", "guides", "update", "updates", "story", "stories", "all", "latest"].some(
    (kw) => cleanQuery.includes(kw)
  );

  if (matches.length === 0 || (isGenericBlogQuery && matches.length < limit)) {
    const fallbackArticles = await prisma.articleCache.findMany({
      where: { shopId: shop.id },
      orderBy: { publishedAt: "desc" },
      take: limit,
    });

    if (matches.length === 0) {
      return fallbackArticles.map(formatArticle);
    }

    // Merge non-duplicate fallback articles to fulfill limit
    const existingIds = new Set(matches.map((m) => m.id));
    for (const fb of fallbackArticles) {
      if (!existingIds.has(fb.id)) {
        matches.push(fb);
        existingIds.add(fb.id);
      }
    }
  }

  matches.sort((a, b) => {
    const aTitleMatch = terms.filter(t => a.title.toLowerCase().includes(t)).length;
    const bTitleMatch = terms.filter(t => b.title.toLowerCase().includes(t)).length;
    if (bTitleMatch !== aTitleMatch) return bTitleMatch - aTitleMatch;

    const aFull = ((a.title || "") + " " + (a.summary || "") + " " + (a.blogTitle || "")).toLowerCase();
    const bFull = ((b.title || "") + " " + (b.summary || "") + " " + (b.blogTitle || "")).toLowerCase();
    const aScore = terms.filter(t => aFull.includes(t)).length;
    const bScore = terms.filter(t => bFull.includes(t)).length;
    return bScore - aScore;
  });

  return matches.slice(0, limit).map(formatArticle);
}

/**
 * Retrieves details and full body of a specific article by handle or shopifyArticleId.
 */
export async function getArticleDetailsService(
  shopDomain: string,
  handleOrId: string
): Promise<StoreArticle | null> {
  const normalizedDomain = shopDomain.toLowerCase().trim();
  const shop = await prisma.shop.findUnique({ where: { shopifyDomain: normalizedDomain } });
  if (!shop) return null;

  const article = await prisma.articleCache.findFirst({
    where: {
      shopId: shop.id,
      OR: [
        { handle: handleOrId },
        { shopifyArticleId: handleOrId },
        { title: { contains: handleOrId } },
      ],
    },
  });

  return article ? formatArticle(article) : null;
}

/**
 * Webhook handler: upsert single article on articles/create, articles/update
 */
export async function upsertSingleArticleCache(shopDomain: string, payload: any) {
  const normalizedDomain = shopDomain.toLowerCase().trim();
  const shop = await prisma.shop.findUnique({ where: { shopifyDomain: normalizedDomain } });
  if (!shop) return null;

  const shopifyArticleId = String(payload.id || payload.shopifyArticleId || "");
  if (!shopifyArticleId) return null;

  const blogHandle = payload.blog_handle || payload.blogHandle || "news";
  const blogTitle = payload.blog_title || payload.blogTitle || "Store Blog";
  const handle = payload.handle || "";
  const cleanSummary = (payload.summary || payload.excerpt || payload.body_html || "")
    .replace(/<[^>]*>?/gm, "")
    .substring(0, 300)
    .trim();
  const cleanContent = (payload.body_html || payload.content || "")
    .replace(/<[^>]*>?/gm, "")
    .trim();
  const imageSrc = payload.image?.src || payload.image?.url || payload.image || "";

  const baseUrl = `https://${normalizedDomain}`;
  const rawUrl = payload.url || payload.online_store_url || `/blogs/${blogHandle}/${handle}`;
  const articleUrl = rawUrl.startsWith("http") ? rawUrl : `${baseUrl}${rawUrl.startsWith("/") ? "" : "/"}${rawUrl}`;

  return await prisma.articleCache.upsert({
    where: {
      shopId_shopifyArticleId: {
        shopId: shop.id,
        shopifyArticleId,
      },
    },
    update: {
      blogHandle,
      blogTitle,
      title: payload.title || "Untitled Article",
      handle,
      summary: cleanSummary,
      content: cleanContent,
      image: imageSrc,
      author: payload.author || "",
      tags: payload.tags || [],
      url: articleUrl,
      publishedAt: payload.published_at ? new Date(payload.published_at) : new Date(),
      updatedAt: new Date(),
    },
    create: {
      shopId: shop.id,
      shopifyArticleId,
      blogHandle,
      blogTitle,
      title: payload.title || "Untitled Article",
      handle,
      summary: cleanSummary,
      content: cleanContent,
      image: imageSrc,
      author: payload.author || "",
      tags: payload.tags || [],
      url: articleUrl,
      publishedAt: payload.published_at ? new Date(payload.published_at) : new Date(),
    },
  });
}

/**
 * Webhook handler: delete article on articles/delete
 */
export async function deleteArticleCache(shopDomain: string, shopifyArticleId: string) {
  const normalizedDomain = shopDomain.toLowerCase().trim();
  const shop = await prisma.shop.findUnique({ where: { shopifyDomain: normalizedDomain } });
  if (!shop) return null;

  return await prisma.articleCache.deleteMany({
    where: {
      shopId: shop.id,
      shopifyArticleId: String(shopifyArticleId),
    },
  });
}

function formatArticle(doc: any): StoreArticle {
  return {
    id: doc.id,
    shopifyArticleId: doc.shopifyArticleId,
    blogHandle: doc.blogHandle || "news",
    blogTitle: doc.blogTitle || "Store Blog",
    title: doc.title,
    handle: doc.handle,
    summary: doc.summary || "",
    content: doc.content || "",
    image: doc.image || "",
    imageUrl: doc.image || "",
    author: doc.author || "",
    tags: doc.tags || [],
    url: doc.url || `https://${doc.shop?.shopifyDomain || 'myshopify.com'}/blogs/${doc.blogHandle || 'news'}/${doc.handle}`,
    publishedAt: doc.publishedAt,
  };
}

