export type PreviewTargetSeed = {
  id: string;
  kind: "content_block" | "heading_group" | "media";
  selector: string;
  sourceHint: string;
  targetIndex: number;
};

function normalizeWhitespace(value: string) {
  return value.replace(/\s+/g, " ").trim();
}

function getBodyHtml(html: string) {
  const bodyMatch = /<body\b[^>]*>([\s\S]*?)<\/body>/i.exec(html);
  return bodyMatch?.[1] ?? html;
}

function getAttribute(attributes: string, name: string) {
  const pattern = new RegExp(`${name}\\s*=\\s*["']([^"']*)["']`, "i");
  return pattern.exec(attributes)?.[1] ?? null;
}

function htmlToPlainText(html: string) {
  return normalizeWhitespace(html.replace(/<script\b[\s\S]*?<\/script>/gi, " ").replace(/<style\b[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " "));
}

function escapeAttribute(value: string) {
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
}

function injectAttribute(openingTag: string, name: string, value: string) {
  if (new RegExp(`\\s${name}\\s*=`, "i").test(openingTag)) {
    return openingTag;
  }

  return openingTag.replace(/\/?\s*>$/, (ending) => ` ${name}="${escapeAttribute(value)}"${ending}`);
}

export function getPreviewTargetSelector(targetId: string) {
  return `[data-seo-target-id="${targetId}"]`;
}

export function getPreviewTargetSeeds(html: string) {
  const bodyHtml = getBodyHtml(html);
  const seeds: PreviewTargetSeed[] = [];
  const blockPattern = /<(section|article|header|main|footer|aside|nav)\b([^>]*)>([\s\S]*?)<\/\1>/gi;
  let blockMatch: RegExpExecArray | null;
  let blockIndex = 1;

  while ((blockMatch = blockPattern.exec(bodyHtml)) && seeds.length < 80) {
    const tagName = blockMatch[1] ?? "section";
    const blockHtml = blockMatch[3] ?? "";
    const text = htmlToPlainText(blockHtml);

    if (text.length < 20) {
      continue;
    }

    const id = `block-${blockIndex}`;
    seeds.push({
      id,
      kind: "content_block",
      selector: getPreviewTargetSelector(id),
      sourceHint: `<${tagName}>`,
      targetIndex: blockIndex
    });
    blockIndex += 1;
  }

  const headingPattern = /<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi;
  let headingMatch: RegExpExecArray | null;
  let headingIndex = 1;

  while ((headingMatch = headingPattern.exec(bodyHtml)) && seeds.length < 120) {
    const text = htmlToPlainText(headingMatch[2] ?? "");

    if (text.length < 2) {
      headingIndex += 1;
      continue;
    }

    const id = `heading-group-${headingIndex}`;
    seeds.push({
      id,
      kind: "heading_group",
      selector: getPreviewTargetSelector(id),
      sourceHint: `H${headingMatch[1] ?? ""}`,
      targetIndex: headingIndex
    });
    headingIndex += 1;
  }

  const imagePattern = /<img\b([^>]*)>/gi;
  let imageMatch: RegExpExecArray | null;
  let imageIndex = 0;
  while ((imageMatch = imagePattern.exec(bodyHtml)) && seeds.length < 180) {
    imageIndex += 1;

    const attributes = imageMatch[1] ?? "";
    const id = `media-alt-${imageIndex}`;
    seeds.push({
      id,
      kind: "media",
      selector: getPreviewTargetSelector(id),
      sourceHint: getAttribute(attributes, "src") ?? `img:nth-of-type(${imageIndex})`,
      targetIndex: imageIndex
    });
  }

  return seeds;
}

export function annotatePreviewTargets(html: string, seeds: PreviewTargetSeed[]) {
  const blockSeeds = seeds.filter((seed) => seed.kind === "content_block");
  const headingSeeds = seeds.filter((seed) => seed.kind === "heading_group");
  const mediaSeeds = seeds.filter((seed) => seed.kind === "media");
  let blockIndex = 0;
  let headingIndex = 0;
  let imageIndex = 0;

  return html
    .replace(/<(section|article|header|main|footer|aside|nav)\b([^>]*)>([\s\S]*?)<\/\1>/gi, (blockHtml, tagName: string, attributes: string, content: string) => {
      const text = htmlToPlainText(content);

      if (text.length < 20) {
        return blockHtml;
      }

      const seed = blockSeeds[blockIndex];
      blockIndex += 1;

      if (!seed) {
        return blockHtml;
      }

      const openingTag = `<${tagName}${attributes}>`;
      return blockHtml.replace(openingTag, injectAttribute(openingTag, "data-seo-target-id", seed.id));
    })
    .replace(/<h([1-6])\b([^>]*)>([\s\S]*?)<\/h\1>/gi, (headingHtml, level: string, attributes: string, content: string) => {
      const text = htmlToPlainText(content);

      if (text.length < 2) {
        return headingHtml;
      }

      const seed = headingSeeds[headingIndex];
      headingIndex += 1;

      if (!seed) {
        return headingHtml;
      }

      const openingTag = `<h${level}${attributes}>`;
      return headingHtml.replace(openingTag, injectAttribute(openingTag, "data-seo-target-id", seed.id));
    })
    .replace(/<img\b([^>]*)>/gi, (openingTag) => {
      imageIndex += 1;
      const seed = mediaSeeds.find((candidate) => candidate.targetIndex === imageIndex);

      return seed ? injectAttribute(openingTag, "data-seo-target-id", seed.id) : openingTag;
    });
}
