import { unstable_cache } from "next/cache";
import { cache } from "react";
import { getProductPriceRangeMap } from "@/lib/medusa/products";
import { sanityClient } from "@/lib/sanity/client";
import type { ProductPersonalizationField } from "@/lib/personalization";
import { SiteTextMap } from "@/lib/site-text";

const SANITY_CACHE_TAG = "sanity";
const SANITY_PUBLIC_REVALIDATE_SECONDS = 1800;
const SANITY_SEARCH_REVALIDATE_SECONDS = 600;
const SANITY_SUGGESTIONS_REVALIDATE_SECONDS = 900;

export type ReassuranceItem = {
  _key?: string;
  icon?: unknown;
  title: string;
  description: string;
};

export type ReassuranceSection = {
  title?: string;
  subtitle?: string;
  items?: ReassuranceItem[];
};

export type HomeContent = {
  heroTitle: string;
  heroSubtitle?: string;
  heroImage?: unknown;
  featuredProducts?: Array<
    SanityProductCard & {
      status?: string | null;
      isActive?: boolean | null;
    }
  >;
  reassurance?: ReassuranceSection;
};

export type ShowroomSlot = {
  _key?: string;
  slug?: string;
  title: string;
  isActive?: boolean;
  description?: string;
  layout?: "portrait" | "square" | "landscape" | "wide";
  coverImage?: unknown;
  coverImageAssetUrl?: string;
  coverImageMimeType?: string;
  coverSvgUrl?: string;
  coverImageUrl?: string;
  alt?: string;
  gallery?: Array<{
    _key?: string;
    image?: unknown;
    imageAssetUrl?: string;
    imageMimeType?: string;
    svgUrl?: string;
    url?: string;
    alt?: string;
  }>;
};

export type ShowroomContent = {
  eyebrow?: string;
  title: string;
  intro?: string;
  seoTitle?: string;
  seoDescription?: string;
  slots?: ShowroomSlot[];
};

export type MenuSettingsItem = {
  _key?: string;
  label: string;
  href: string;
  kind?: "link" | "shopDropdown";
  isVisible?: boolean;
  openInNewTab?: boolean;
  children?: Array<{
    _key?: string;
    label: string;
    href: string;
    isVisible?: boolean;
    openInNewTab?: boolean;
  }>;
};

export type MenuSettingsContent = {
  title?: string;
  items?: MenuSettingsItem[];
};

export type InvoiceSettingsContent = {
  companyName: string;
  companyAddress?: string;
  companyEmail?: string;
  companyPhone?: string;
  vatNumber?: string;
  logoUrl?: string;
  invoiceTitle?: string;
  footerNote?: string;
  paymentTerms?: string;
  accentHexColor?: string;
  invoiceNumberPrefixTemplate?: string;
};

export type QuoteSettingsContent = {
  companyName: string;
  companyAddress?: string;
  companyEmail?: string;
  companyPhone?: string;
  vatNumber?: string;
  logoUrl?: string;
  quoteTitle?: string;
  quoteNumberPrefixTemplate?: string;
  introText?: string;
  footerNote?: string;
  paymentTerms?: string;
  accentHexColor?: string;
};

type SiteTextEntry = {
  key?: string;
  value?: string;
};

type SiteTextFolderItem = {
  key?: string;
  value?: string;
};

type SiteTextFolder = {
  title?: string;
  items?: SiteTextFolderItem[];
};

export type SanityProductCard = {
  _id: string;
  title: string;
  handle: string;
  displayBadge?: string;
  imageUrl?: string;
  priceCents?: number | null;
  minPriceCents?: number | null;
  maxPriceCents?: number | null;
  regularPriceCents?: number | null;
  salePriceCents?: number | null;
};

export type SanityThemeOption = {
  title: string;
  imageUrl?: string;
};

export type SanityProductSuggestion = SanityProductCard;

export type SanityProductCategory = {
  _id: string;
  title: string;
  slug: string;
  productCount: number;
};

export type SanityPortableTextMarkDef = {
  _key: string;
  _type: string;
  href?: string;
};

export type SanityPortableTextSpan = {
  _key?: string;
  _type: "span";
  text?: string;
  marks?: string[];
};

export type SanityPortableTextBlock = {
  _key?: string;
  _type: "block";
  style?: string;
  listItem?: "bullet" | "number";
  level?: number;
  children?: SanityPortableTextSpan[];
  markDefs?: SanityPortableTextMarkDef[];
};

export type SanityPage = {
  _id: string;
  title: string;
  slug: string;
  seoTitle?: string;
  seoDescription?: string;
  content?: SanityPortableTextBlock[];
};

export type SanityProductDetail = SanityProductCard & {
  shortDescription?: string;
  careText?: string;
  length?: number | null;
  width?: number | null;
  height?: number | null;
  isCustomizable?: boolean;
  showPaperFinish?: boolean;
  paperFinishRequired?: boolean;
  showThemes?: boolean;
  themeOptions?: string[];
  themeDetails?: SanityThemeOption[];
  personalizationTextField?: {
    isEnabled?: boolean;
    label?: string;
    showLabel?: boolean;
    helperText?: string;
    placeholder?: string;
    defaultValue?: string;
    maxLength?: number | null;
    required?: boolean;
  };
  customThemeRequest?: {
    isEnabled?: boolean;
    optionLabel?: string;
    modalTitle?: string;
    helperText?: string;
    modalDescription?: string;
  };
  variantDescriptions?: Array<{
    title?: string;
    sku?: string;
    description?: string;
    descriptionRich?: SanityPortableTextBlock[];
    selections?: Array<{ axisKey?: string; value?: string }>;
  }>;
  personalizationFields?: ProductPersonalizationField[];
  description?: string;
  descriptionRich?: SanityPortableTextBlock[];
  regularPriceCents?: number | null;
  salePriceCents?: number | null;
  stockStatus?: string;
  categories?: Array<{ title: string; slug?: string }>;
  gallery?: Array<{ url?: string; alt?: string }>;
};

type StorefrontCategory = {
  _id: string;
  title: string;
  slug: string;
};

type ProductTaxonomyInput = {
  title?: string;
  handle?: string;
  tags?: string[];
  categoryTitles?: string[];
};

const STOREFRONT_CATEGORIES: StorefrontCategory[] = [
  {_id: "store-category-gourmandises", title: "Gourmandises", slug: "gourmandises"},
  {_id: "store-category-papeteries", title: "Papeteries", slug: "papeteries"},
  {_id: "store-category-stickers", title: "Stickers", slug: "stickers"},
  {_id: "store-category-nos-livrets", title: "Nos livrets", slug: "nos-livrets"},
  {_id: "store-category-objets-personnalises", title: "Objets personnalisés", slug: "objets-personnalises"},
];

function logSanityQueryError(scope: string, error: unknown) {
  console.error(`[sanity] ${scope} failed`, error);
}

function normalizeText(value: string) {
  return value
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase();
}

function includesAny(text: string, terms: string[]) {
  return terms.some((term) => text.includes(term));
}

function classifyProductToCategorySlug(product: ProductTaxonomyInput) {
  const searchable = normalizeText(
    [
      product.title || "",
      product.handle || "",
      ...(product.tags || []),
      ...(product.categoryTitles || []),
    ].join(" "),
  );

  if (includesAny(searchable, ["livret", "abecedaire", "table de", "tables d", "cahier", "coloriage"])) {
    return "nos-livrets";
  }

  if (includesAny(searchable, ["sticker", "stickers", "etiquette", "etiquettes"])) {
    return "stickers";
  }

  if (
    includesAny(searchable, [
      "bonbon",
      "bonbons",
      "chocolat",
      "kinder",
      "haribo",
      "daim",
      "smarties",
      "nutella",
      "kitkat",
      "knoppers",
      "snickers",
      "confiture",
      "gourmand",
      "friandise",
      "dragibus",
      "mms",
    ])
  ) {
    return "gourmandises";
  }

  if (
    includesAny(searchable, [
      "carte",
      "marque-place",
      "guirlande",
      "fanion",
      "cube",
      "pyramide",
      "boite a crayons",
      "boite-a-crayons",
      "bulles de savon",
    ])
  ) {
    return "papeteries";
  }

  return "objets-personnalises";
}

function toStorefrontProductCategoryCountMap() {
  const map = new Map<string, number>();
  for (const category of STOREFRONT_CATEGORIES) {
    map.set(category.slug, 0);
  }
  return map;
}

function toStorefrontCategoriesWithCounts(counts: Map<string, number>): SanityProductCategory[] {
  return STOREFRONT_CATEGORIES.map((category) => ({
    ...category,
    productCount: counts.get(category.slug) || 0,
  }));
}

function asProductCard(product: SanityProductCard & { tags?: string[]; categoryTitles?: string[] }) {
  const { _id, title, handle, displayBadge, imageUrl, priceCents, minPriceCents, maxPriceCents, regularPriceCents, salePriceCents } =
    product;
  return {
    _id,
    title,
    handle,
    displayBadge: typeof displayBadge === "string" && displayBadge.trim() ? displayBadge.trim() : undefined,
    imageUrl,
    priceCents,
    minPriceCents,
    maxPriceCents,
    regularPriceCents,
    salePriceCents,
  };
}

async function withMedusaPriceRanges(products: SanityProductCard[]): Promise<SanityProductCard[]> {
  if (products.length === 0) {
    return products;
  }

  try {
    const priceRangeMap = await getProductPriceRangeMap(products.map((product) => product.handle));

    return products.map((product) => {
      const range = priceRangeMap.get(product.handle);
      if (!range) {
        return product;
      }

      const minPriceCents = range.minPriceCents;
      const maxPriceCents = range.maxPriceCents;
      const hasRange = typeof minPriceCents === "number" && typeof maxPriceCents === "number";

      return {
        ...product,
        priceCents: hasRange ? minPriceCents : product.priceCents,
        minPriceCents: hasRange ? minPriceCents : product.minPriceCents,
        maxPriceCents: hasRange ? maxPriceCents : product.maxPriceCents,
      };
    });
  } catch (error) {
    console.error("[sanity] medusa price range enrichment failed", error);
    return products;
  }
}

async function listLegacyClassifiedProductCategories(
  client: NonNullable<typeof sanityClient>,
  limit = 20,
): Promise<SanityProductCategory[]> {
  const products = await client.fetch<Array<{ title?: string; handle?: string; tags?: string[]; categoryTitles?: string[] }>>(
    `*[
      _type == "product" &&
      defined(slug.current) &&
      status == "publish" &&
      coalesce(isActive, true) == true
    ]{
      title,
      "handle": slug.current,
      tags,
      "categoryTitles": categories[]->title
    }`,
  );

  const counts = toStorefrontProductCategoryCountMap();
  for (const product of products) {
    const slug = classifyProductToCategorySlug(product);
    counts.set(slug, (counts.get(slug) || 0) + 1);
  }

  return toStorefrontCategoriesWithCounts(counts).slice(0, limit);
}

async function getHomeContentUncached(): Promise<HomeContent | null> {
  if (!sanityClient) {
    return null;
  }

  try {
    return await sanityClient.fetch<HomeContent | null>(
      `*[_type == "homePage"] | order(_updatedAt desc)[0]{
        heroTitle,
        heroSubtitle,
        heroImage,
        "featuredProducts": featuredProducts[]->{
          _id,
          title,
          status,
          "isActive": coalesce(isActive, true),
          "handle": slug.current,
          displayBadge,
          "imageUrl": coalesce(imageUrl, mainImage.asset->url, gallery[0].url, gallery[0].image.asset->url),
          "priceCents": coalesce(salePriceCents, priceCents, regularPriceCents),
          regularPriceCents,
          salePriceCents
        },
        reassurance{
          title,
          subtitle,
          items[]{
            _key,
            icon,
            title,
            description
          }
        }
      }`,
    );
  } catch (error) {
    logSanityQueryError("getHomeContent", error);
    return null;
  }
}

const getHomeContentCached = unstable_cache(getHomeContentUncached, ["sanity-home-content"], {
  revalidate: SANITY_PUBLIC_REVALIDATE_SECONDS,
  tags: [SANITY_CACHE_TAG],
});

export const getHomeContent = cache(async (): Promise<HomeContent | null> => {
  const content = await getHomeContentCached();

  if (!content?.featuredProducts?.length) {
    return content;
  }

  return {
    ...content,
    featuredProducts: await withMedusaPriceRanges(content.featuredProducts),
  };
});

async function getShowroomContentUncached(): Promise<ShowroomContent | null> {
  if (!sanityClient) {
    return null;
  }

  try {
    return await sanityClient.fetch<ShowroomContent | null>(
      `*[_type == "showroomPage" && _id == "showroom-page"][0]{
        eyebrow,
        title,
        intro,
        seoTitle,
        seoDescription,
        "slots": slots[]{
          _key,
          "slug": slug.current,
          title,
          "isActive": coalesce(isActive, true),
          description,
          layout,
          coverImage,
          "coverImageAssetUrl": coverImage.asset->url,
          "coverImageMimeType": coverImage.asset->mimeType,
          "coverSvgUrl": coverSvg.asset->url,
          coverImageUrl,
          alt,
          "gallery": gallery[]{
            _key,
            image,
            "imageAssetUrl": image.asset->url,
            "imageMimeType": image.asset->mimeType,
            "svgUrl": svg.asset->url,
            url,
            alt
          }
        }
      }`,
    );
  } catch (error) {
    logSanityQueryError("getShowroomContent", error);
    return null;
  }
}

const getShowroomContentCached = unstable_cache(getShowroomContentUncached, ["sanity-showroom-content"], {
  revalidate: SANITY_PUBLIC_REVALIDATE_SECONDS,
  tags: [SANITY_CACHE_TAG],
});

export const getShowroomContent = cache(async (): Promise<ShowroomContent | null> => getShowroomContentCached());

async function getMenuSettingsContentUncached(): Promise<MenuSettingsContent | null> {
  if (!sanityClient) {
    return null;
  }

  try {
    return await sanityClient.fetch<MenuSettingsContent | null>(
      `*[_type == "menuSettings" && _id == "menu-settings"][0]{
        title,
        "items": items[]{
          _key,
          label,
          href,
          kind,
          "isVisible": coalesce(isVisible, true),
          "openInNewTab": coalesce(openInNewTab, false)
          ,
          "children": children[]{
            _key,
            label,
            href,
            "isVisible": coalesce(isVisible, true),
            "openInNewTab": coalesce(openInNewTab, false)
          }
        }
      }`,
    );
  } catch (error) {
    logSanityQueryError("getMenuSettingsContent", error);
    return null;
  }
}

const getMenuSettingsContentCached = unstable_cache(getMenuSettingsContentUncached, ["sanity-menu-settings"], {
  revalidate: SANITY_PUBLIC_REVALIDATE_SECONDS,
  tags: [SANITY_CACHE_TAG],
});

export const getMenuSettingsContent = cache(
  async (): Promise<MenuSettingsContent | null> => getMenuSettingsContentCached(),
);

async function getInvoiceSettingsContentUncached(): Promise<InvoiceSettingsContent | null> {
  if (!sanityClient) {
    return null;
  }

  return sanityClient.fetch<InvoiceSettingsContent | null>(
    `*[_type == "invoiceSettings" && _id == "invoice-settings"][0]{
      companyName,
      companyAddress,
      companyEmail,
      companyPhone,
      vatNumber,
      logoUrl,
      invoiceTitle,
      footerNote,
      paymentTerms,
      accentHexColor,
      invoiceNumberPrefixTemplate
    }`,
  );
}

const getInvoiceSettingsContentCached = unstable_cache(getInvoiceSettingsContentUncached, ["sanity-invoice-settings"], {
  revalidate: SANITY_PUBLIC_REVALIDATE_SECONDS,
  tags: [SANITY_CACHE_TAG],
});

export const getInvoiceSettingsContent = cache(
  async (): Promise<InvoiceSettingsContent | null> => getInvoiceSettingsContentCached(),
);

async function getQuoteSettingsContentUncached(): Promise<QuoteSettingsContent | null> {
  if (!sanityClient) {
    return null;
  }

  return sanityClient.fetch<QuoteSettingsContent | null>(
    `*[_type == "quoteSettings" && _id == "quote-settings"][0]{
      companyName,
      companyAddress,
      companyEmail,
      companyPhone,
      vatNumber,
      logoUrl,
      quoteTitle,
      quoteNumberPrefixTemplate,
      introText,
      footerNote,
      paymentTerms,
      accentHexColor
    }`,
  );
}

const getQuoteSettingsContentCached = unstable_cache(getQuoteSettingsContentUncached, ["sanity-quote-settings"], {
  revalidate: SANITY_PUBLIC_REVALIDATE_SECONDS,
  tags: [SANITY_CACHE_TAG],
});

export const getQuoteSettingsContent = cache(
  async (): Promise<QuoteSettingsContent | null> => getQuoteSettingsContentCached(),
);

async function getSiteTextContentUncached(): Promise<SiteTextMap | null> {
  if (!sanityClient) {
    return null;
  }

  let data: { folders?: SiteTextFolder[]; entries?: SiteTextEntry[] } | null;
  try {
    data = await sanityClient.fetch<{ folders?: SiteTextFolder[]; entries?: SiteTextEntry[] } | null>(
      `*[_type == "siteTextSettings" && _id == "site-text-settings"][0]{
        folders[]{
          title,
          items[]{key, value}
        },
        entries[]{key, value}
      }`,
    );
  } catch (error) {
    logSanityQueryError("getSiteTextContent", error);
    return null;
  }

  const fromFolders = (data?.folders || []).reduce<SiteTextMap>((acc, folder) => {
    const items = Array.isArray(folder.items) ? folder.items : [];
    for (const item of items) {
      const key = (item.key || "").trim();
      const value = (item.value || "").trim();
      if (!key || !value) {
        continue;
      }
      acc[key] = value;
    }
    return acc;
  }, {});

  const fromEntries = (data?.entries || []).reduce<SiteTextMap>((acc, entry) => {
    const key = (entry.key || "").trim();
    const value = (entry.value || "").trim();
    if (!key || !value) {
      return acc;
    }
    acc[key] = value;
    return acc;
  }, {});

  const merged = {
    ...fromEntries,
    ...fromFolders,
  };

  if (!Object.keys(merged).length) {
    return null;
  }

  return merged;
}

const getSiteTextContentCached = unstable_cache(getSiteTextContentUncached, ["sanity-site-text"], {
  revalidate: SANITY_PUBLIC_REVALIDATE_SECONDS,
  tags: [SANITY_CACHE_TAG],
});

export const getSiteTextContent = cache(async (): Promise<SiteTextMap | null> => getSiteTextContentCached());

async function getSanityPageBySlugUncached(slug: string, documentId?: string): Promise<SanityPage | null> {
  if (!sanityClient) {
    return null;
  }

  const normalizedSlug = slug.trim();
  const normalizedDocumentId = (documentId || "").trim();
  if (!normalizedSlug && !normalizedDocumentId) {
    return null;
  }

  return sanityClient.fetch<SanityPage | null>(
    `*[
      _type == "page" &&
      (
        slug.current == $slug ||
        _id == $documentId
      )
    ][0]{
      _id,
      title,
      "slug": slug.current,
      seoTitle,
      seoDescription,
      content
    }`,
    { slug: normalizedSlug, documentId: normalizedDocumentId },
  );
}

const getSanityPageBySlugCached = unstable_cache(getSanityPageBySlugUncached, ["sanity-page-by-slug"], {
  revalidate: SANITY_PUBLIC_REVALIDATE_SECONDS,
  tags: [SANITY_CACHE_TAG],
});

export const getSanityPageBySlug = cache(async (slug: string, documentId?: string): Promise<SanityPage | null> => {
  return getSanityPageBySlugCached(slug.trim(), (documentId || "").trim());
});

async function listSanityProductsUncached(limit = 24): Promise<SanityProductCard[]> {
  if (!sanityClient) {
    return [];
  }

  try {
    return await sanityClient.fetch<SanityProductCard[]>(
      `*[_type == "product" && defined(slug.current) && status == "publish" && coalesce(isActive, true) == true] | order(title asc)[0...$limit]{
        _id,
        title,
        "handle": slug.current,
        displayBadge,
        "imageUrl": coalesce(imageUrl, mainImage.asset->url, gallery[0].url, gallery[0].image.asset->url),
        "priceCents": coalesce(salePriceCents, priceCents, regularPriceCents),
        regularPriceCents,
        salePriceCents
      }`,
      { limit },
    );
  } catch (error) {
    logSanityQueryError("listSanityProducts", error);
    return [];
  }
}

const listSanityProductsCached = unstable_cache(listSanityProductsUncached, ["sanity-products"], {
  revalidate: SANITY_PUBLIC_REVALIDATE_SECONDS,
  tags: [SANITY_CACHE_TAG],
});

export const listSanityProducts = cache(async (limit = 24): Promise<SanityProductCard[]> => {
  return withMedusaPriceRanges(await listSanityProductsCached(limit));
});

async function listSanityProductCategoriesUncached(limit = 20): Promise<SanityProductCategory[]> {
  if (!sanityClient) {
    return [];
  }

  try {
    const categories = await sanityClient.fetch<SanityProductCategory[]>(
      `*[
        _type == "productCategory" &&
        defined(slug.current) &&
        coalesce(isActive, true) == true
      ] | order(title asc)[0...$limit]{
        _id,
        title,
        "slug": slug.current,
        "productCount": count(*[
          _type == "product" &&
          defined(slug.current) &&
          status == "publish" &&
          coalesce(isActive, true) == true &&
          references(^._id)
        ])
      }`,
      { limit },
    );

    if (categories.length > 0) {
      return categories;
    }

    return await listLegacyClassifiedProductCategories(sanityClient, limit);
  } catch (error) {
    logSanityQueryError("listSanityProductCategories", error);
    return [];
  }
}

const listSanityProductCategoriesCached = unstable_cache(
  listSanityProductCategoriesUncached,
  ["sanity-product-categories"],
  { revalidate: SANITY_PUBLIC_REVALIDATE_SECONDS, tags: [SANITY_CACHE_TAG] },
);

export const listSanityProductCategories = cache(async (limit = 20): Promise<SanityProductCategory[]> => {
  return listSanityProductCategoriesCached(limit);
});

async function listSanityProductsByCategoryUncached(
  categorySlug: string,
  limit = 60,
): Promise<SanityProductCard[]> {
  if (!sanityClient) {
    return [];
  }

  const slug = categorySlug.trim();
  if (!slug) {
    return [];
  }

  const products = await sanityClient.fetch<SanityProductCard[]>(
    `*[
      _type == "product" &&
      defined(slug.current) &&
      status == "publish" &&
      coalesce(isActive, true) == true &&
      count(categories[@->slug.current == $slug && coalesce(@->isActive, true) == true]) > 0
    ] | order(title asc)[0...$limit]{
      _id,
      title,
      "handle": slug.current,
      displayBadge,
      "imageUrl": coalesce(imageUrl, mainImage.asset->url, gallery[0].url, gallery[0].image.asset->url),
      "priceCents": coalesce(salePriceCents, priceCents, regularPriceCents),
      regularPriceCents,
      salePriceCents
    }`,
    { slug, limit },
  );

  if (products.length > 0) {
    return products;
  }

  const categoryExists = await sanityClient.fetch<boolean>(
    `count(*[_type == "productCategory" && slug.current == $slug]) > 0`,
    { slug },
  );

  if (categoryExists) {
    return [];
  }

  const legacyProducts = await sanityClient.fetch<Array<SanityProductCard & { tags?: string[]; categoryTitles?: string[] }>>(
    `*[
        _type == "product" &&
        defined(slug.current) &&
        status == "publish" &&
        coalesce(isActive, true) == true
    ] | order(title asc)[0...400]{
      _id,
      title,
      "handle": slug.current,
      displayBadge,
      "imageUrl": coalesce(imageUrl, mainImage.asset->url, gallery[0].url, gallery[0].image.asset->url),
      "priceCents": coalesce(salePriceCents, priceCents, regularPriceCents),
      regularPriceCents,
      salePriceCents,
      tags,
      "categoryTitles": categories[]->title
    }`,
  );

  return legacyProducts
    .filter((product) => classifyProductToCategorySlug(product) === slug)
    .slice(0, limit)
    .map(asProductCard);
}

function toSearchPattern(query: string) {
  return `*${query
    .trim()
    .replace(/\s+/g, "*")
    .toLowerCase()}*`;
}

async function searchSanityProductsUncached(query: string, limit = 60): Promise<SanityProductCard[]> {
  if (!sanityClient) {
    return [];
  }

  const normalized = query.trim();

  if (!normalized) {
    return [];
  }

  const pattern = toSearchPattern(normalized);

  return sanityClient.fetch<SanityProductCard[]>(
    `*[
      _type == "product" &&
      defined(slug.current) &&
      status == "publish" &&
      coalesce(isActive, true) == true &&
      (
        lower(title) match $pattern ||
        lower(coalesce(shortDescription, "")) match $pattern ||
        lower(coalesce(pt::text(descriptionRich), description, "")) match $pattern ||
        lower(slug.current) match $pattern ||
        lower(array::join(coalesce(tags, []), " ")) match $pattern
      )
    ] | order(title asc)[0...$limit]{
      _id,
      title,
      "handle": slug.current,
      displayBadge,
      "imageUrl": coalesce(imageUrl, mainImage.asset->url, gallery[0].url, gallery[0].image.asset->url),
      "priceCents": coalesce(salePriceCents, priceCents, regularPriceCents),
      regularPriceCents,
      salePriceCents
    }`,
    { pattern, limit },
  );
}

const searchSanityProductsCached = unstable_cache(searchSanityProductsUncached, ["sanity-search-products"], {
  revalidate: SANITY_SEARCH_REVALIDATE_SECONDS,
  tags: [SANITY_CACHE_TAG],
});

export const searchSanityProducts = cache(async (query: string, limit = 60): Promise<SanityProductCard[]> => {
  return withMedusaPriceRanges(await searchSanityProductsCached(query.trim(), limit));
});

async function searchSanityProductSuggestionsUncached(
  query: string,
  limit = 6,
): Promise<SanityProductSuggestion[]> {
  if (!sanityClient) {
    return [];
  }

  const normalized = query.trim();

  if (normalized.length < 2) {
    return [];
  }

  const pattern = toSearchPattern(normalized);

  return sanityClient.fetch<SanityProductSuggestion[]>(
    `*[
      _type == "product" &&
      defined(slug.current) &&
      status == "publish" &&
      coalesce(isActive, true) == true &&
      (
        lower(title) match $pattern ||
        lower(coalesce(shortDescription, "")) match $pattern ||
        lower(coalesce(pt::text(descriptionRich), description, "")) match $pattern ||
        lower(slug.current) match $pattern ||
        lower(array::join(coalesce(tags, []), " ")) match $pattern
      )
    ] | order(title asc)[0...$limit]{
      _id,
      title,
      "handle": slug.current,
      displayBadge,
      "imageUrl": coalesce(imageUrl, mainImage.asset->url, gallery[0].url, gallery[0].image.asset->url),
      "priceCents": coalesce(salePriceCents, priceCents, regularPriceCents),
      regularPriceCents,
      salePriceCents
    }`,
    { pattern, limit },
  );
}

const searchSanityProductSuggestionsCached = unstable_cache(
  searchSanityProductSuggestionsUncached,
  ["sanity-search-suggestions"],
  { revalidate: SANITY_SUGGESTIONS_REVALIDATE_SECONDS, tags: [SANITY_CACHE_TAG] },
);

export const searchSanityProductSuggestions = cache(
  async (query: string, limit = 6): Promise<SanityProductSuggestion[]> => {
    return searchSanityProductSuggestionsCached(query.trim(), limit);
  },
);

async function getSanityProductByHandleUncached(handle: string): Promise<SanityProductDetail | null> {
  if (!sanityClient) {
    return null;
  }

  const product = await sanityClient.fetch<
    (SanityProductDetail & {
      tags?: string[];
      descriptionPlain?: string;
    }) | null
  >(
    `*[_type == "product" && slug.current == $handle && coalesce(isActive, true) == true][0]{
      _id,
      title,
      "handle": slug.current,
      displayBadge,
      "imageUrl": coalesce(imageUrl, mainImage.asset->url, gallery[0].url, gallery[0].image.asset->url),
      "priceCents": coalesce(salePriceCents, priceCents, regularPriceCents),
      regularPriceCents,
      salePriceCents,
      shortDescription,
      careText,
      length,
      width,
      height,
      "isCustomizable": coalesce(isCustomizable, true),
      "showPaperFinish": coalesce(showPaperFinish, true),
      "paperFinishRequired": coalesce(paperFinishRequired, false),
      "showThemes": coalesce(showThemes, false),
      "themeDetails": select(
        showThemes == true => themes[defined(@->title) && coalesce(@->isActive, true) == true]->{
          title,
          imageUrl
        },
        []
      ),
      "themeOptions": select(
        showThemes == true => themes[defined(@->title) && coalesce(@->isActive, true) == true]->title,
        []
      ),
      "personalizationFields": personalizationFields[]{
        label,
        "key": select(defined(key.current) => key.current, key),
        inputType,
        useThemeOptions,
        "options": select(
          useThemeOptions == true && showThemes == true => themes[defined(@->title) && coalesce(@->isActive, true) == true]->title,
          options
        ),
        helperText,
        "helperImageUrl": helperImage.asset->url,
        helperImageLabel,
        showLabel,
        placeholder,
        defaultValue,
        maxLength,
        required
      },
      "personalizationTextField": personalizationTextField{
        "isEnabled": coalesce(isEnabled, true),
        label,
        "showLabel": coalesce(showLabel, false),
        helperText,
        placeholder,
        defaultValue,
        maxLength,
        "required": coalesce(required, false)
      },
      "customThemeRequest": customThemeRequest{
        "isEnabled": coalesce(isEnabled, true),
        optionLabel,
        modalTitle,
        helperText,
        modalDescription
      },
      "variantDescriptions": coalesce(variantDefinitions[coalesce(isEnabled, true) == true && (defined(description) || count(descriptionRich) > 0)]{
        title,
        sku,
        description,
        descriptionRich,
        selections[]{
          axisKey,
          value
        }
      }, []) + coalesce(variations[defined(description) || count(descriptionRich) > 0]{
        "title": select(count(attributes[defined(option)]) > 0 => array::join(attributes[defined(option)].option, " / "), sku),
        sku,
        description,
        descriptionRich,
        "selections": attributes[]{
          "axisKey": coalesce(slug, name),
          "value": option
        }
      }, []),
      descriptionRich,
      "descriptionPlain": coalesce(pt::text(descriptionRich), description),
      stockStatus,
      "gallery": gallery[]{
        "url": coalesce(url, image.asset->url),
        alt
      }[defined(url) || defined(image.asset->url)],
      tags,
      categories[@->isActive != false]->{
        title,
        "slug": slug.current
      }
    }`,
    { handle },
  );

  if (!product) {
    return null;
  }

  const computedCategorySlug = classifyProductToCategorySlug({
    title: product.title,
    handle: product.handle,
    tags: product.tags || [],
    categoryTitles: (product.categories || []).map((category) => category.title),
  });
  const computedCategory = STOREFRONT_CATEGORIES.find((category) => category.slug === computedCategorySlug);

  return {
    _id: product._id,
    title: product.title,
    handle: product.handle,
    displayBadge: typeof product.displayBadge === "string" && product.displayBadge.trim() ? product.displayBadge.trim() : undefined,
    imageUrl: product.imageUrl,
    priceCents: product.priceCents,
    regularPriceCents: product.regularPriceCents,
    salePriceCents: product.salePriceCents,
    shortDescription: product.shortDescription,
    careText: product.careText,
    isCustomizable: product.isCustomizable,
    showPaperFinish: product.showPaperFinish !== false,
    paperFinishRequired: product.paperFinishRequired === true,
    showThemes: product.showThemes === true,
    themeOptions: Array.isArray(product.themeOptions) ? product.themeOptions.filter((value) => typeof value === "string") : [],
    themeDetails: Array.isArray(product.themeDetails)
      ? product.themeDetails
          .filter((value) => value && typeof value.title === "string")
          .map((value) => ({
            title: value.title,
            imageUrl: typeof value.imageUrl === "string" ? value.imageUrl : undefined,
          }))
      : [],
    personalizationTextField:
      product.personalizationTextField && typeof product.personalizationTextField === "object"
        ? product.personalizationTextField
        : undefined,
    customThemeRequest:
      product.customThemeRequest && typeof product.customThemeRequest === "object"
        ? product.customThemeRequest
        : undefined,
    variantDescriptions: Array.isArray(product.variantDescriptions)
      ? product.variantDescriptions.filter(Boolean)
      : [],
    personalizationFields: Array.isArray(product.personalizationFields)
      ? product.personalizationFields.filter(Boolean)
      : [],
    descriptionRich: product.descriptionRich,
    description: product.descriptionPlain || product.description,
    stockStatus: product.stockStatus,
    gallery: product.gallery,
    categories:
      product.categories && product.categories.length > 0
        ? product.categories
        : computedCategory
          ? [{ title: computedCategory.title, slug: computedCategory.slug }]
          : [],
  };
}

const getSanityProductByHandleCached = unstable_cache(getSanityProductByHandleUncached, ["sanity-product-by-handle"], {
  revalidate: 300,
  tags: [SANITY_CACHE_TAG],
});

export const getSanityProductByHandle = cache(async (handle: string): Promise<SanityProductDetail | null> => {
  return getSanityProductByHandleCached(handle.trim());
});

export const listSanityProductsByCategory = cache(
  async (categorySlug: string, limit = 60): Promise<SanityProductCard[]> => {
    return withMedusaPriceRanges(await listSanityProductsByCategoryCached(categorySlug.trim(), limit));
  },
);

async function listSanityProductHandlesUncached(limit = 500): Promise<string[]> {
  if (!sanityClient) {
    return [];
  }

  const rows = await sanityClient.fetch<Array<{ handle?: string }>>(
    `*[_type == "product" && defined(slug.current) && status == "publish" && coalesce(isActive, true) == true] | order(title asc)[0...$limit]{
      "handle": slug.current
    }`,
    { limit },
  );

  return rows.map((row) => row.handle || "").filter(Boolean);
}

const listSanityProductsByCategoryCached = unstable_cache(
  listSanityProductsByCategoryUncached,
  ["sanity-products-by-category"],
  { revalidate: SANITY_PUBLIC_REVALIDATE_SECONDS, tags: [SANITY_CACHE_TAG] },
);

const listSanityProductHandlesCached = unstable_cache(listSanityProductHandlesUncached, ["sanity-product-handles"], {
  revalidate: 3600,
  tags: [SANITY_CACHE_TAG],
});

export const listSanityProductHandles = cache(async (limit = 500): Promise<string[]> => {
  return listSanityProductHandlesCached(limit);
});

export type SanityProductSeoRow = {
  _id: string;
  title: string;
  handle: string;
  shortDescription?: string;
  description?: string;
  imageUrl?: string;
  gallery?: Array<{ url?: string; alt?: string }>;
};

async function listSanityProductSeoRowsUncached(limit = 1000): Promise<SanityProductSeoRow[]> {
  if (!sanityClient) {
    return [];
  }

  return sanityClient.fetch<SanityProductSeoRow[]>(
    `*[_type == "product" && defined(slug.current) && status == "publish" && coalesce(isActive, true) == true] | order(title asc)[0...$limit]{
      _id,
      title,
      "handle": slug.current,
      shortDescription,
      "description": coalesce(pt::text(descriptionRich), description),
      "imageUrl": coalesce(imageUrl, mainImage.asset->url, gallery[0].url, gallery[0].image.asset->url),
      "gallery": gallery[]{
        "url": coalesce(url, image.asset->url),
        alt
      }[defined(url) || defined(image.asset->url)]
    }`,
    { limit },
  );
}

const listSanityProductSeoRowsCached = unstable_cache(listSanityProductSeoRowsUncached, ["sanity-seo-rows"], {
  revalidate: 1800,
  tags: [SANITY_CACHE_TAG],
});

export const listSanityProductSeoRows = cache(async (limit = 1000): Promise<SanityProductSeoRow[]> => {
  return listSanityProductSeoRowsCached(limit);
});
