/**
 * Visual Annotation — Visible Element Detector
 * ──────────────────────────────────────────────
 * Parses serialized DOM to detect visible page elements relevant to SEO and
 * conversion. Uses regex/string heuristics (no external DOM library needed).
 *
 * Internal module — vendor names permitted in code; forbidden in user-facing output.
 */

export interface BoundingBox {
  x: number;
  y: number;
  width: number;
  height: number;
}

export interface DetectedElements {
  hasFAQAccordion: boolean;
  hasFAQAccordionInViewport: boolean;
  faqAccordionBoundingBox: BoundingBox | null;

  primaryCTAs: Array<{
    text: string;
    boundingBox: BoundingBox | null;
    isInViewport: boolean;
    ctaType: "phone" | "email" | "form" | "shop" | "unknown";
  }>;
  ctaCount: number;
  ctaCountInViewport: number;

  hasReviewWidget: boolean;
  reviewWidgetType: "star-rating" | "testimonial" | "badge" | null;
  reviewWidgetBoundingBox: BoundingBox | null;

  hasTrustBadges: boolean;
  trustBadges: Array<{
    type: "certification" | "award" | "membership" | "guarantee";
    boundingBox: BoundingBox | null;
  }>;

  hasContactForm: boolean;
  contactFormBoundingBox: BoundingBox | null;

  headingHierarchy: Array<{ level: number; text: string; boundingBox: BoundingBox | null }>;
  hasH1: boolean;
  multipleH1s: boolean;

  visibleSchemaIndicators: Array<{
    indicatorType:
      | "star-ratings"
      | "price-display"
      | "event-info"
      | "recipe-info"
      | "product-info"
      | "breadcrumbs";
    boundingBox: BoundingBox | null;
  }>;

  imageCount: number;
  imagesWithoutAlt: number;

  pageLoadIndicators: {
    hasHeroSection: boolean;
    heroHasHeadline: boolean;
    heroHasSubheadline: boolean;
    heroHasCTA: boolean;
    heroBoundingBox: BoundingBox | null;
  };
}

// ─── Synthetic bounding boxes ────────────────────────────────────────────────
// We extract structural signals from DOM strings rather than a live browser;
// bounding boxes here are positional estimates for annotation placement.

function estimateViewportBox(index: number, total: number, viewWidth = 1280, viewHeight = 720): BoundingBox {
  const segment = viewHeight / Math.max(total, 1);
  return { x: 20, y: Math.round(index * segment) + 10, width: viewWidth - 40, height: Math.max(segment - 20, 40) };
}

// ─── CTA detection helpers ────────────────────────────────────────────────────

const CTA_ACTION_RE =
  /\b(get a quote|get quote|free quote|book now|book a|schedule|call now|call us|contact us|get started|sign up|register|shop now|buy now|order now|add to cart|request a|start free|try free|demo|free trial|learn more|see pricing|view plans|explore)\b/i;

function classifyCtaType(text: string, href: string): "phone" | "email" | "form" | "shop" | "unknown" {
  if (/tel:|(\+1[\s.-]?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}/.test(text + href)) return "phone";
  if (/mailto:|email/i.test(text + href)) return "email";
  if (/\b(shop|buy|order|cart|purchase)\b/i.test(text + href)) return "shop";
  if (/\b(contact|schedule|book|request|get started|sign up|register|form)\b/i.test(text + href)) return "form";
  return "unknown";
}

// ─── Main detector ────────────────────────────────────────────────────────────

export function detectVisibleElements(dom: string): DetectedElements {
  const lower = dom.toLowerCase();

  // ── Headings ──────────────────────────────────────────────────────────────
  const headingHierarchy: DetectedElements["headingHierarchy"] = [];
  for (let level = 1; level <= 6; level++) {
    const tagRe = new RegExp(`<h${level}[^>]*>([\\s\\S]*?)<\\/h${level}>`, "gi");
    let m: RegExpExecArray | null;
    let idx = 0;
    while ((m = tagRe.exec(dom)) !== null) {
      const text = m[1].replace(/<[^>]+>/g, "").trim().slice(0, 120);
      if (text) {
        headingHierarchy.push({
          level,
          text,
          boundingBox: estimateViewportBox(idx, 5),
        });
        idx++;
      }
    }
  }
  const h1s = headingHierarchy.filter((h) => h.level === 1);

  // ── FAQ detection ─────────────────────────────────────────────────────────
  const hasFAQAccordion =
    /\bfaq\b|frequently asked/i.test(dom) ||
    /<details[^>]*>/i.test(dom) ||
    /aria-expanded|accordion/i.test(dom);
  const faqAboveFold =
    hasFAQAccordion && /<details|aria-expanded/i.test(dom.slice(0, Math.floor(dom.length * 0.3)));

  // ── CTA detection ─────────────────────────────────────────────────────────
  const ctaMatches: DetectedElements["primaryCTAs"] = [];
  const anchorRe = /<a\s[^>]*href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi;
  const buttonRe = /<button[^>]*>([\s\S]*?)<\/button>/gi;
  let m: RegExpExecArray | null;

  while ((m = anchorRe.exec(dom)) !== null) {
    const href = m[1];
    const text = m[2].replace(/<[^>]+>/g, "").trim().slice(0, 80);
    if (text && CTA_ACTION_RE.test(text)) {
      const posRatio = m.index / dom.length;
      ctaMatches.push({
        text,
        boundingBox: estimateViewportBox(ctaMatches.length, 6),
        isInViewport: posRatio < 0.25,
        ctaType: classifyCtaType(text, href),
      });
    }
  }
  while ((m = buttonRe.exec(dom)) !== null) {
    const text = m[1].replace(/<[^>]+>/g, "").trim().slice(0, 80);
    if (text && CTA_ACTION_RE.test(text)) {
      const posRatio = m.index / dom.length;
      ctaMatches.push({
        text,
        boundingBox: estimateViewportBox(ctaMatches.length, 6),
        isInViewport: posRatio < 0.25,
        ctaType: classifyCtaType(text, ""),
      });
    }
  }

  // ── Review widgets ────────────────────────────────────────────────────────
  const hasStarRating = /\bstar.?rating|rating.?star|aggregaterating|itemprop=.?rating/i.test(dom);
  const hasTrustpilot = /trustpilot|elfsight-app|widget-reviews|reviews\.io/i.test(lower);
  const hasTestimonial = /\btestimonial|review-card|client.?review|patient.?review/i.test(lower);
  const hasReviewWidget = hasStarRating || hasTrustpilot || hasTestimonial;
  const reviewWidgetType: DetectedElements["reviewWidgetType"] = hasStarRating
    ? "star-rating"
    : hasTestimonial
    ? "testimonial"
    : hasTrustpilot
    ? "badge"
    : null;

  // ── Trust badges ──────────────────────────────────────────────────────────
  const trustBadges: DetectedElements["trustBadges"] = [];
  if (/bbb|better business bureau/i.test(dom)) trustBadges.push({ type: "certification", boundingBox: null });
  if (/angi|angies list|homeadvisor/i.test(dom)) trustBadges.push({ type: "membership", boundingBox: null });
  if (/award|winner|best of|top rated/i.test(dom)) trustBadges.push({ type: "award", boundingBox: null });
  if (/guarantee|satisfaction guaranteed|money.?back/i.test(lower)) trustBadges.push({ type: "guarantee", boundingBox: null });
  if (/certified|accredited|licensed|insured/i.test(lower) && trustBadges.length < 3) {
    trustBadges.push({ type: "certification", boundingBox: null });
  }

  // ── Contact form ──────────────────────────────────────────────────────────
  const hasContactForm =
    /<form[^>]*>/i.test(dom) &&
    /type=.?(email|tel|text)|contact|inquiry|message/i.test(dom);

  // ── Images ────────────────────────────────────────────────────────────────
  const imgTags = (dom.match(/<img\s[^>]*>/gi) ?? []);
  const imagesWithoutAlt = imgTags.filter(
    (tag) => !/alt=["'][^"']+["']/i.test(tag),
  ).length;

  // ── Visible schema indicators ─────────────────────────────────────────────
  const visibleSchemaIndicators: DetectedElements["visibleSchemaIndicators"] = [];
  if (hasStarRating) visibleSchemaIndicators.push({ indicatorType: "star-ratings", boundingBox: null });
  if (/\$\d|\bprice\b|from \$|starting at/i.test(dom)) {
    visibleSchemaIndicators.push({ indicatorType: "price-display", boundingBox: null });
  }
  if (/breadcrumb/i.test(lower)) {
    visibleSchemaIndicators.push({ indicatorType: "breadcrumbs", boundingBox: null });
  }
  if (/\bevent\b.*\bdate\b|\bdate\b.*\bevent\b/i.test(dom)) {
    visibleSchemaIndicators.push({ indicatorType: "event-info", boundingBox: null });
  }

  // ── Hero section ──────────────────────────────────────────────────────────
  const hasHeroSection = /\bhero\b|hero-section|banner-section|site-header|masthead/i.test(lower);
  const heroHasHeadline = h1s.length > 0;
  const heroHasSubheadline =
    headingHierarchy.some((h) => h.level === 2) ||
    /<p[^>]*class=["'][^"']*subtitle[^"']*["']/i.test(dom);
  const heroHasCTA = ctaMatches.some((c) => c.isInViewport);

  return {
    hasFAQAccordion,
    hasFAQAccordionInViewport: faqAboveFold,
    faqAccordionBoundingBox: hasFAQAccordion ? { x: 20, y: 400, width: 1240, height: 80 } : null,

    primaryCTAs: ctaMatches.slice(0, 10),
    ctaCount: ctaMatches.length,
    ctaCountInViewport: ctaMatches.filter((c) => c.isInViewport).length,

    hasReviewWidget,
    reviewWidgetType,
    reviewWidgetBoundingBox: hasReviewWidget ? { x: 20, y: 500, width: 600, height: 100 } : null,

    hasTrustBadges: trustBadges.length > 0,
    trustBadges,

    hasContactForm,
    contactFormBoundingBox: hasContactForm ? { x: 200, y: 350, width: 880, height: 300 } : null,

    headingHierarchy,
    hasH1: h1s.length > 0,
    multipleH1s: h1s.length > 1,

    visibleSchemaIndicators,
    imageCount: imgTags.length,
    imagesWithoutAlt,

    pageLoadIndicators: {
      hasHeroSection,
      heroHasHeadline,
      heroHasSubheadline,
      heroHasCTA,
      heroBoundingBox: hasHeroSection ? { x: 0, y: 0, width: 1280, height: 400 } : null,
    },
  };
}
