import shopify from "./shopify.js";

/**
 * SEO core: we mirror each published group into a metafield on every member
 * product. The theme block then renders crawlable <a href> sibling links in
 * Liquid (server-side), so search engines see the cross-links between products.
 *
 * Namespace/key: linked_products.group  (type: json)
 * Value shape: { label, style, members: [{ handle, title, value, color, image, current }] }
 *
 * Note: `current` can't be known at write time (it depends on which product page
 * is viewed), so the Liquid template determines "current" itself by comparing
 * handles. We store the full sibling list on every member.
 */
const NAMESPACE = "linked_products";
const KEY = "group";

function clientFor(session) {
  return new shopify.api.clients.Graphql({ session });
}

const DEFINITION_MUTATION = `
  mutation createDef($definition: MetafieldDefinitionInput!) {
    metafieldDefinitionCreate(definition: $definition) {
      createdDefinition { id }
      userErrors { field message code }
    }
  }`;

let definitionEnsured = false;

/**
 * Ensure a metafield definition exists for linked_products.group with
 * storefront/theme access. Without a definition, the value is NOT exposed to
 * Liquid (product.metafields.linked_products.group is empty), which is why the
 * theme block renders nothing. Idempotent: a "taken" error means it exists.
 */
export async function ensureDefinition(session) {
  if (definitionEnsured) return;
  const client = clientFor(session);
  try {
    const resp = await client.request(DEFINITION_MUTATION, {
      variables: {
        definition: {
          name: "Linked products group",
          namespace: NAMESPACE,
          key: KEY,
          description: "Sibling products linked for cross-linking and SEO.",
          type: "json",
          ownerType: "PRODUCT",
          access: { storefront: "PUBLIC_READ" },
        },
      },
    });
    const errs = resp.data?.metafieldDefinitionCreate?.userErrors || [];
    const onlyTaken = errs.every((e) => e.code === "TAKEN");
    if (errs.length && !onlyTaken) {
      console.error("metafieldDefinitionCreate errors:", JSON.stringify(errs));
    }
  } catch (e) {
    console.error("ensureDefinition error:", e.message);
  }
  definitionEnsured = true;
}

function buildValue(group) {
  return JSON.stringify({
    label: group.label || "Color",
    style: group.style || "color",
    members: (group.members || []).map((m) => ({
      productId: m.product_id,
      handle: m.handle,
      title: m.product_title,
      value: m.swatch_value,
      color: m.swatch_color,
      image: m.image,
    })),
  });
}

const SET_MUTATION = `
  mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
    metafieldsSet(metafields: $metafields) {
      userErrors { field message }
    }
  }`;

// Plural metafieldsDelete is stable and takes identifiers in one call.
const DELETE_MUTATION = `
  mutation metafieldsDelete($metafields: [MetafieldIdentifierInput!]!) {
    metafieldsDelete(metafields: $metafields) {
      deletedMetafields { ownerId namespace key }
      userErrors { field message }
    }
  }`;

/** Write the group metafield to every member product. */
export async function syncGroupMetafields(session, group) {
  await ensureDefinition(session);
  const client = clientFor(session);
  const value = buildValue(group);

  // metafieldsSet accepts up to 25 per call; chunk to be safe.
  const inputs = (group.members || []).map((m) => ({
    ownerId: m.product_id,
    namespace: NAMESPACE,
    key: KEY,
    type: "json",
    value,
  }));

  for (let i = 0; i < inputs.length; i += 25) {
    const chunk = inputs.slice(i, i + 25);
    const resp = await client.request(SET_MUTATION, {
      variables: { metafields: chunk },
    });
    const errs = resp.data?.metafieldsSet?.userErrors || [];
    if (errs.length) {
      console.error("metafieldsSet errors:", JSON.stringify(errs));
    }
  }
}

/** Remove the group metafield from a set of product GIDs. */
export async function clearGroupMetafields(session, productIds) {
  const ids = productIds || [];
  if (!ids.length) return;
  const client = clientFor(session);

  const identifiers = ids.map((ownerId) => ({
    ownerId,
    namespace: NAMESPACE,
    key: KEY,
  }));

  // Delete in chunks of 25 (mutation limit).
  for (let i = 0; i < identifiers.length; i += 25) {
    const chunk = identifiers.slice(i, i + 25);
    const resp = await client.request(DELETE_MUTATION, {
      variables: { metafields: chunk },
    });
    const errs = resp.data?.metafieldsDelete?.userErrors || [];
    if (errs.length) {
      console.error("metafieldsDelete errors:", JSON.stringify(errs));
    }
  }
}