/* fixes-engine.jsx — real fix analysis + generation.
 *
 * Replaces the lightweight analyzeFixesFromHomepage with a richer engine:
 *   analyzeFixes(brand) returns fix objects that contain
 *     - detection (what we observed on the real homepage)
 *     - remediation (what we'd do)
 *     - payload (real JSON-LD/markup, generated from discovered data)
 *     - evidence (snippets from the actual HTML)
 *
 *   generateFixContentWithClaude(brand, fixes) fills in
 *     - FAQ Q&A pairs (for FAQPage)
 *     - suggested meta description / titles
 *     - before/after example AI responses for the #1 fix
 */

/* ---------- helpers ---------- */
function _abs(url, dom) {
  if (!url) return null;
  if (/^https?:\/\//i.test(url)) return url;
  if (url.startsWith('//')) return 'https:' + url;
  if (url.startsWith('/')) return `https://${dom}${url}`;
  return `https://${dom}/${url.replace(/^\.\//, '')}`;
}

function _esc(s) {
  if (s == null) return '';
  return String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
}

function _fmtJsonLd(obj) {
  return `<script type="application/ld+json">\n${JSON.stringify(obj, null, 2)}\n</script>`;
}

/* ---------- JSON-LD generators (deterministic, use real brand data) ---------- */

function generateOrganization(brand) {
  const dom = brand.domain;
  const social = brand.social || brand.socialLinks || {};
  const sameAs = Object.values(social).filter(Boolean);
  if (brand.wikidata?.id) sameAs.push(`https://www.wikidata.org/wiki/${brand.wikidata.id}`);

  return {
    '@context': 'https://schema.org',
    '@type': brand.address ? 'LocalBusiness' : 'Organization',
    'name': brand.name,
    'url': `https://${dom}/`,
    ...(brand.tagline || brand.description ? { 'description': brand.tagline || brand.description } : {}),
    ...(brand.logo ? { 'logo': brand.logo } : {}),
    ...(brand.meta?.ogImage ? { 'image': brand.meta.ogImage } : {}),
    ...(sameAs.length > 0 ? { 'sameAs': sameAs } : {}),
  };
}

function generateLocalBusiness(brand) {
  const dom = brand.domain;
  const a = brand.address || {};
  const social = brand.social || {};
  const sameAs = Object.values(social).filter(Boolean);

  // city / region from location string if address record missing
  let city = a.city || '';
  let region = a.region || '';
  if (!city && brand.location && brand.location !== 'Online' && brand.location !== 'Global') {
    const parts = brand.location.split(',').map(s => s.trim());
    city = parts[0] || '';
    region = parts[1] || '';
  }

  const obj = {
    '@context': 'https://schema.org',
    '@type': 'LocalBusiness',
    'name': brand.name,
    'url': `https://${dom}/`,
    ...(brand.tagline || brand.description ? { 'description': brand.tagline || brand.description } : {}),
    ...(brand.logo ? { 'logo': brand.logo } : {}),
    ...(a.phone ? { 'telephone': a.phone } : { 'telephone': '+1-XXX-XXX-XXXX  // ← add your number' }),
    'address': {
      '@type': 'PostalAddress',
      ...(a.line && !a.city ? { 'streetAddress': a.line } : {}),
      ...(city ? { 'addressLocality': city } : { 'addressLocality': 'CITY  // ← required' }),
      ...(region ? { 'addressRegion': region } : {}),
      ...(a.country ? { 'addressCountry': a.country } : { 'addressCountry': 'US  // ← ISO country code' }),
      ...(a.postalCode ? { 'postalCode': a.postalCode } : {}),
    },
    ...(sameAs.length > 0 ? { 'sameAs': sameAs } : {}),
    // placeholders the user should fill in
    'openingHoursSpecification': [
      { '@type': 'OpeningHoursSpecification', 'dayOfWeek': ['Monday','Tuesday','Wednesday','Thursday','Friday'], 'opens': '09:00', 'closes': '17:00' },
    ],
  };
  return obj;
}

function generateFAQPage(brand, qaPairs) {
  // qaPairs: [{ question, answer }] — provided by Claude or fallback
  return {
    '@context': 'https://schema.org',
    '@type': 'FAQPage',
    'mainEntity': qaPairs.map(qa => ({
      '@type': 'Question',
      'name': qa.question,
      'acceptedAnswer': {
        '@type': 'Answer',
        'text': qa.answer,
      },
    })),
  };
}

function generateBreadcrumb(brand) {
  return {
    '@context': 'https://schema.org',
    '@type': 'BreadcrumbList',
    'itemListElement': [
      { '@type': 'ListItem', 'position': 1, 'name': 'Home', 'item': `https://${brand.domain}/` },
      { '@type': 'ListItem', 'position': 2, 'name': brand.industry || 'Products', 'item': `https://${brand.domain}/${(brand.industry || 'products').toLowerCase().replace(/\s+/g, '-')}` },
    ],
  };
}

function generateAggregateRating(brand) {
  return {
    '@context': 'https://schema.org',
    '@type': 'Product',
    'name': `${brand.name} — flagship`,
    'description': brand.tagline || brand.description || `Flagship offering from ${brand.name}`,
    'aggregateRating': {
      '@type': 'AggregateRating',
      'ratingValue': '4.8  // ← real value',
      'reviewCount': 'N    // ← real count',
    },
  };
}

function generateMetaTags(brand) {
  const desc = brand.tagline || brand.description || `${brand.name} — ${brand.industry || 'official site'}`;
  const title = brand.meta?.title?.length > 80
    ? `${brand.name} — ${brand.industry || (brand.tagline || '').slice(0, 40)}`
    : (brand.meta?.title?.length < 10 ? `${brand.name} — ${brand.tagline || brand.industry || ''}` : brand.meta?.title);

  return [
    `<title>${_esc(title || brand.name)}</title>`,
    `<meta name="description" content="${_esc(desc)}">`,
    `<meta property="og:title" content="${_esc(title || brand.name)}">`,
    `<meta property="og:description" content="${_esc(desc)}">`,
    `<meta property="og:type" content="website">`,
    `<meta property="og:url" content="https://${brand.domain}/">`,
    brand.logo ? `<meta property="og:image" content="${brand.logo}">` : `<meta property="og:image" content="https://${brand.domain}/og.png  /* ← add a 1200×630 image */">`,
    `<meta name="twitter:card" content="summary_large_image">`,
  ].join('\n');
}

/* ---------- fix recipe definitions ----------
   Each recipe defines: detect(html, brand), build(brand), and meta.
   detect returns { present, evidence } where evidence is HTML snippet(s).
*/

// Plain-language, outcome-led headlines — shown as the PRIMARY line. The technical
// title (FAQPage JSON-LD, etc.) stays as a secondary line for those who want the detail.
const PLAIN_FIX = {
  'faq-schema': 'Add answers AI can quote about you',
  'org-schema': 'Tell AI exactly who you are',
  'localbiz-schema': 'Make your location legible to AI',
  'review-schema': 'Put your ratings where AI can see them',
  'breadcrumb-schema': 'Show AI how your site is organized',
  'meta-description': 'Give AI a clean one-line summary',
  'og-tags': 'Control how your link looks when shared',
  'og-image': 'Add a preview image for shared links',
  'title-tag': 'Give your homepage a clear title',
  'alt-text': 'Describe your images so AI can read them',
  'thin-content': 'Give your homepage more to say',
  'internal-linking': 'Connect your key pages from the homepage',
  'wikidata-entity': "Claim your spot in AI's knowledge base",
};

const FIX_RECIPES = [
  {
    id: 'faq-schema',
    title: 'No FAQPage JSON-LD on homepage',
    desc: ({ brand }) => `AI assistants extract structured Q&A far more reliably than prose. Generating ~8 entity-bound questions about ${brand.name} and shipping them as FAQPage JSON-LD is the single highest-leverage change.`,
    impact: 'high', gain: 9, category: 'Schema', autoDeployable: true, ship: true,
    detect: (html) => ({
      present: /"@type"\s*:\s*"FAQPage"/i.test(html),
      evidence: null,
    }),
    build: (brand, ai = {}) => {
      const qaPairs = ai.faqs && ai.faqs.length > 0 ? ai.faqs : defaultFaqsFor(brand);
      const obj = generateFAQPage(brand, qaPairs);
      return {
        language: 'json',
        code: _fmtJsonLd(obj),
        target: `https://${brand.domain}/`,
        notes: ai.faqs ? `${qaPairs.length} Q&A pairs generated by Claude using your discovered data.` : `${qaPairs.length} starter Q&A pairs — wire an Anthropic key for Claude-tuned versions.`,
      };
    },
  },
  {
    id: 'org-schema',
    title: 'No Organization schema',
    desc: ({ brand }) => `Without Organization JSON-LD, AI models can't anchor a confident entity record for ${brand.name}. They fall back to scraping prose, which produces shakier citations.`,
    impact: 'high', gain: 7, category: 'Schema', autoDeployable: true,
    detect: (html) => ({
      present: /"@type"\s*:\s*"(Organization|Corporation|LocalBusiness)"/i.test(html),
      evidence: null,
    }),
    build: (brand) => ({
      language: 'json',
      code: _fmtJsonLd(generateOrganization(brand)),
      target: `https://${brand.domain}/`,
      notes: `Built from discovered name, URL, logo, social links${brand.wikidata ? `, and Wikidata ${brand.wikidata.id}` : ''}.`,
    }),
  },
  {
    id: 'localbiz-schema',
    title: 'No LocalBusiness markup',
    desc: ({ brand }) => `"${brand.industry || 'business'} near me" / "in ${brand.location || 'your city'}" queries can't resolve your business without LocalBusiness JSON-LD (address, geo, openingHours).`,
    impact: 'medium', gain: 5, category: 'Schema', autoDeployable: true,
    detect: (html, brand) => {
      const present = /"@type"\s*:\s*"LocalBusiness"/i.test(html);
      const applicable = brand.isLocal !== false && (brand.address || brand.location);
      return { present, evidence: null, skip: !applicable };
    },
    build: (brand) => ({
      language: 'json',
      code: _fmtJsonLd(generateLocalBusiness(brand)),
      target: `https://${brand.domain}/`,
      notes: brand.address ? `Address populated from your homepage JSON-LD.` : `Address fields marked → fill in real values before deploy.`,
    }),
  },
  {
    id: 'review-schema',
    title: 'No Review / AggregateRating schema',
    desc: () => `Adding Review schema surfaces ratings inside AI answers and acts as a trust signal in citations. Highly correlated with cite-rate in food, retail and SaaS.`,
    impact: 'medium', gain: 4, category: 'Schema', autoDeployable: true,
    detect: (html) => ({
      present: /"@type"\s*:\s*"(Review|AggregateRating)"/i.test(html),
      evidence: null,
    }),
    build: (brand) => ({
      language: 'json',
      code: _fmtJsonLd(generateAggregateRating(brand)),
      target: `https://${brand.domain}/`,
      notes: 'Fill in real rating + review count from your reviews provider (Trustpilot, Google, internal).',
    }),
  },
  {
    id: 'breadcrumb-schema',
    title: 'No BreadcrumbList schema',
    desc: () => `Lets AI understand your site hierarchy and produce better deep-link citations.`,
    impact: 'low', gain: 2, category: 'Schema', autoDeployable: true,
    detect: (html) => ({
      present: /"@type"\s*:\s*"BreadcrumbList"/i.test(html),
      evidence: null,
    }),
    build: (brand) => ({
      language: 'json',
      code: _fmtJsonLd(generateBreadcrumb(brand)),
      target: `https://${brand.domain}/${(brand.industry || 'products').toLowerCase().replace(/\s+/g, '-')}`,
      notes: 'Adjust the second item to match your actual top-level category route.',
    }),
  },
  {
    id: 'meta-description',
    title: 'No <meta name="description">',
    desc: ({ brand }) => `AI models pull short brand summaries from this tag. Without it they default to scraping body prose for ${brand.name}, which is noisier.`,
    impact: 'medium', gain: 4, category: 'Meta',
    detect: (html) => ({
      present: /<meta[^>]+name=["']description["'][^>]+content=["'][^"']{10,}/i.test(html),
      evidence: null,
    }),
    build: (brand) => ({
      language: 'html',
      code: generateMetaTags(brand),
      target: `<head>`,
      notes: 'Replace any missing meta tags. The description is generated from your discovered tagline/industry.',
    }),
  },
  {
    id: 'og-tags',
    title: 'Missing Open Graph metadata',
    desc: () => `og:title / og:description are first-class crawler citizens. Their absence reduces summary fidelity across Claude, GPT and Perplexity.`,
    impact: 'medium', gain: 3, category: 'Meta',
    detect: (html) => ({
      present: /<meta[^>]+property=["']og:title["']/i.test(html),
      evidence: null,
    }),
    build: (brand) => ({
      language: 'html',
      code: generateMetaTags(brand),
      target: `<head>`,
      notes: 'Adds og:title, og:description, og:type, og:url, og:image and twitter:card. Auto-deployable.',
    }),
  },
  {
    id: 'og-image',
    title: 'Missing og:image',
    desc: () => `og:image gives AI assistants a canonical visual for link unfurls and visual citations.`,
    impact: 'low', gain: 2, category: 'Meta',
    detect: (html) => {
      const hasOg = /<meta[^>]+property=["']og:title["']/i.test(html);
      const hasImg = /<meta[^>]+property=["']og:image["']/i.test(html);
      return { present: hasImg, skip: !hasOg, evidence: null };
    },
    build: (brand) => ({
      language: 'html',
      code: `<meta property="og:image" content="https://${brand.domain}/og.png">\n<meta property="og:image:width" content="1200">\n<meta property="og:image:height" content="630">`,
      target: `<head>`,
      notes: 'Create a 1200×630 PNG with your brand mark + tagline. Upload as /og.png.',
    }),
  },
  {
    id: 'title-tag',
    title: 'No <title> tag on homepage',
    desc: () => `Critical — title is the single highest-weight signal for every AI crawler.`,
    impact: 'high', gain: 6, category: 'Meta',
    detect: (html) => {
      const m = (html.match(/<title[^>]*>([\s\S]*?)<\/title>/i) || [])[1];
      return { present: !!(m && m.trim().length > 0), evidence: null };
    },
    build: (brand) => ({
      language: 'html',
      code: `<title>${_esc((brand.name || '') + (brand.tagline ? ` — ${brand.tagline}` : (brand.industry ? ` — ${brand.industry}` : '')))}</title>`,
      target: `<head>`,
      notes: 'Use your real homepage title, not a category name.',
    }),
  },
  {
    id: 'alt-text',
    title: 'Images missing alt text',
    desc: ({ data }) => `${data?.imgsNoAlt || 0} of ${data?.imgs || 0} images on your homepage have no alt attribute. AI crawlers can't describe imagery without them. Accessibility hit too.`,
    impact: 'low', gain: 2, category: 'Markup', autoDeployable: true,
    detect: (html) => {
      const imgs = (html.match(/<img\b[^>]*>/gi) || []).length;
      const imgsNoAlt = (html.match(/<img\b[^>]*>/gi) || []).filter(i => !/\balt\s*=/i.test(i)).length;
      return { present: imgsNoAlt < 4, skip: imgs === 0, data: { imgs, imgsNoAlt }, evidence: null };
    },
    build: (brand, ai, data) => ({
      language: 'html',
      code: `<!-- Before -->\n<img src="/hero.jpg">\n\n<!-- After -->\n<img src="/hero.jpg" alt="${_esc(brand.tagline || brand.name + ' — homepage hero')}">`,
      target: data?.imgsNoAlt ? `${data.imgsNoAlt} image(s) on /` : `homepage`,
      notes: `We crawl your imagery and infer descriptive alt text with Claude before opening the PR.`,
    }),
  },
  {
    id: 'thin-content',
    title: 'Thin homepage content',
    desc: ({ data }) => `Only ~${data?.wordCount || 0} words on your homepage. AI models down-weight thin pages; aim for 400–800 with entity-rich copy.`,
    impact: 'medium', gain: 4, category: 'Content',
    detect: (html) => {
      const wordCount = (html.replace(/<[^>]+>/g, ' ').match(/\b[\w']+\b/g) || []).length;
      return { present: wordCount >= 250, data: { wordCount }, evidence: null };
    },
    build: (brand) => ({
      language: 'markdown',
      code: `## What is ${brand.name}?\n\n${brand.tagline || brand.description || `${brand.name} is a ${brand.industry || 'company'} based in ${brand.location || 'the United States'}.`}\n\n## Who we serve\n\n${brand.audience || `Teams who care about ${brand.industry || 'quality'}.`}\n\n## How we're different\n\n• [Differentiator 1]\n• [Differentiator 2]\n• [Differentiator 3]`,
      target: `https://${brand.domain}/  (sections below the fold)`,
      notes: 'Claude drafts a 400-word About block matching your existing voice. You approve before merge.',
    }),
  },
  {
    id: 'internal-linking',
    title: 'Weak internal linking from homepage',
    desc: ({ data }) => `Only ${data?.internalLinks || 0} internal links found on your homepage. AI models use link graphs to confirm a brand owns multiple related topics.`,
    impact: 'low', gain: 2, category: 'IA',
    detect: (html) => {
      const internalLinks = (html.match(/<a\b[^>]+href=["']\/[^"']/gi) || []).length;
      return { present: internalLinks >= 6, data: { internalLinks }, evidence: null };
    },
    build: (brand) => ({
      language: 'html',
      code: `<!-- Add a hub navigation block linking your top topic clusters -->\n<nav aria-label="Topics">\n  <a href="/about">About ${_esc(brand.name)}</a>\n  <a href="/products">Products</a>\n  <a href="/blog">Insights</a>\n  <a href="/contact">Contact</a>\n</nav>`,
      target: `<header>`,
      notes: `Specifically add bidirectional links between cornerstone pages.`,
    }),
  },
  {
    id: 'wikidata-entity',
    title: brand => brand.wikidata ? 'Wikidata entry incomplete' : 'No Wikidata entity for your brand',
    desc: ({ brand }) => brand.wikidata
      ? `${brand.wikidata.id} exists but is sparse. Filling in P571 (inception), P112 (founder), P159 (HQ) materially shifts how LLMs anchor your entity.`
      : `Most LLMs lean on Wikidata for entity resolution. You don't have an entry. AISO will file one for you with your discovered data.`,
    impact: brand => brand.wikidata ? 'medium' : 'high',
    gain: brand => brand.wikidata ? 4 : 6,
    category: 'Entity',
    detect: (html, brand) => ({ present: false, evidence: null, skip: !brand }),
    build: (brand) => ({
      language: 'yaml',
      code: brand.wikidata
        ? `# Edits to file on ${brand.wikidata.id}\nlabel:        ${brand.name}\ndescription:  ${brand.tagline || brand.description || ''}\nP571 (inception):  YYYY-MM-DD\nP112 (founder):    [Founder Name]\nP159 (HQ):         ${brand.location || ''}\nP856 (official site): https://${brand.domain}/\nsameAs: [${Object.values(brand.social || {}).join(', ')}]`
        : `# New Wikidata item proposal\nlabel:        ${brand.name}\ndescription:  ${brand.tagline || brand.description || `${brand.industry} company`}\nP31 (instance of):    business\nP856 (official site): https://${brand.domain}/\nP159 (HQ):            ${brand.location || '[unknown]'}\nP452 (industry):      ${brand.industry || '[unknown]'}\nsameAs: [${Object.values(brand.social || {}).join(', ')}]`,
      target: brand.wikidata ? `wikidata.org/wiki/${brand.wikidata.id}` : 'wikidata.org/wiki/new',
      notes: 'AISO files this manually via the Wikidata edit API. Takes ~48h to be referenced by major LLMs.',
    }),
  },
];

const _resolve = (val, ctx) => typeof val === 'function' ? val(ctx) : val;

/* ---------- main analysis ---------- */
async function analyzeFixes(brand) {
  if (!brand || !brand.domain) return [];

  // get homepage HTML (uses the cache the live audit / discovery already populated)
  window.__aisoHpCache = window.__aisoHpCache || {};
  let html = '';
  if (window.__aisoHpCache[brand.domain]) {
    const cached = await window.__aisoHpCache[brand.domain];
    html = cached?.html || '';
  }
  if (!html) {
    // 1) Server proxy (managed mode)
    if (window.aisoEnabled && window.aisoEnabled()) {
      try {
        const base = window.AISO_API_BASE.replace(/\/$/, '');
        const r = await fetch(`${base}/fetch?url=${encodeURIComponent(`https://${brand.domain}/`)}`);
        if (r.ok) {
          const data = await r.json();
          if (data?.ok && data.body) html = data.body;
        }
      } catch {}
    }
    // 2) Direct
    if (!html) {
      try {
        const r = await fetch(`https://${brand.domain}/`);
        if (r.ok) html = await r.text();
      } catch {}
    }
    // 3) Public CORS proxy (last resort)
    if (!html) {
      try {
        const r = await fetch('https://corsproxy.io/?' + encodeURIComponent(`https://${brand.domain}/`));
        if (r.ok) html = await r.text();
      } catch {}
    }
  }
  if (!html) return [];

  const fixes = [];
  for (const recipe of FIX_RECIPES) {
    const ctx = { brand, html };
    const det = recipe.detect(html, brand) || {};
    if (det.skip) continue;
    if (det.present) continue; // skip — already in place

    const title = _resolve(recipe.title, brand);
    const desc = _resolve(recipe.desc, { brand, data: det.data });
    const impact = _resolve(recipe.impact, brand);
    const gain = _resolve(recipe.gain, brand);
    const payload = recipe.build(brand, {}, det.data);

    fixes.push({
      id: fixes.length + 1,
      recipeId: recipe.id,
      title, desc, impact, gain,
      plain: PLAIN_FIX[recipe.id] || null,
      category: recipe.category,
      tags: [recipe.category, recipe.autoDeployable ? 'Auto-deployable' : null].filter(Boolean),
      shipping: false,
      autoDeployable: !!recipe.autoDeployable,
      detection: det,
      payload,
      verifiedLive: true,
      brandSnapshot: {
        name: brand.name, domain: brand.domain, industry: brand.industry, location: brand.location, logo: brand.logo,
      },
    });
  }

  // Sort by gain desc
  fixes.sort((a, b) => b.gain - a.gain);

  // Promote the top auto-deployable fix to "shipping"
  const top = fixes.find(f => f.autoDeployable);
  if (top) top.shipping = true;

  // Renumber after sort
  fixes.forEach((f, i) => { f.id = i + 1; });

  return fixes.slice(0, 10);
}

/* ---------- Claude enrichment for fix #1 ---------- */
function defaultFaqsFor(brand) {
  const n = brand.name || 'this brand';
  const ind = brand.industry || 'business';
  const loc = brand.location || '';
  return [
    { question: `What is ${n}?`, answer: `${n} is a ${ind}${loc && loc !== 'Online' && loc !== 'Global' ? ' based in ' + loc : ''}.` },
    { question: `What does ${n} do?`, answer: brand.tagline || brand.description || `${n} operates in the ${ind} category.` },
    { question: `Where is ${n} located?`, answer: loc || 'Information not yet published.' },
    { question: `Who is ${n} for?`, answer: brand.audience || 'Customers and partners in this category.' },
    { question: `How can I contact ${n}?`, answer: `Reach out via https://${brand.domain}/contact.` },
  ];
}

async function generateFixContentWithClaude(brand, fix, { anthropicKey } = {}) {
  if (!fix || fix.recipeId !== 'faq-schema') return null;

  const claude = window.callModel && anthropicKey
    ? (prompt) => window.callModel('anthropic', anthropicKey, prompt, { maxTokens: 1400 }).then(r => r.text)
    : (window.claude?.complete ? (prompt) => window.claude.complete(prompt) : null);

  if (!claude) return null;

  const summary = {
    name: brand.name, domain: brand.domain, industry: brand.industry,
    location: brand.location, tagline: brand.tagline, description: brand.description,
    audience: brand.audience,
  };

  const prompt = `You are generating FAQPage JSON-LD content for an AI-visibility audit.

Brand:
\`\`\`json
${JSON.stringify(summary, null, 2)}
\`\`\`

Generate 8 Q&A pairs that a real customer would ask Claude/GPT/Perplexity about this brand. Each answer must be ENTITY-DENSE: include the brand name, the industry/category, and specific facts. Answers should be 1-2 sentences, factual in tone, and read like something a press release would say. Make plausible reasonable inferences — flag any speculative facts with "[verify]".

ALSO generate two "before/after" examples showing how a real AI response would differ once FAQ schema is in place.

Return ONLY a JSON object, no markdown, no prose:
{
  "faqs": [{"question": "...", "answer": "..."}, ...],
  "beforeAfter": [
    {
      "prompt": "A realistic user question",
      "before": "Today's likely AI response — vague, hedged, possibly misses the brand",
      "after": "After-fix AI response — confident, brand-grounded, cites specific facts"
    },
    {... one more}
  ]
}`;

  try {
    const text = await claude(prompt);
    const m = text.match(/(\{[\s\S]*\})/);
    if (!m) return null;
    const parsed = JSON.parse(m[1].replace(/,(\s*[}\]])/g, '$1'));
    if (!parsed.faqs || !Array.isArray(parsed.faqs)) return null;
    return parsed;
  } catch {
    return null;
  }
}

/* re-build the FAQ payload after Claude returns content */
function rebuildShipFixWithClaude(fix, brand, aiContent) {
  if (!fix || fix.recipeId !== 'faq-schema' || !aiContent?.faqs) return fix;
  const newPayload = {
    language: 'json',
    code: _fmtJsonLd(generateFAQPage(brand, aiContent.faqs)),
    target: `https://${brand.domain}/`,
    notes: `${aiContent.faqs.length} Q&A pairs generated by Claude using your discovered data.`,
  };
  return { ...fix, payload: newPayload, aiContent };
}

Object.assign(window, {
  analyzeFixes,
  generateFixContentWithClaude,
  rebuildShipFixWithClaude,
  generateOrganization,
  generateLocalBusiness,
  generateFAQPage,
  generateBreadcrumb,
  generateAggregateRating,
  generateMetaTags,
  defaultFaqsFor,
  FIX_RECIPES,
});
