// Shared parsing/validation helpers for the EDM checks, so each check doesn't
// re-implement DOM walking and URL classification.

import type { CheckVariable } from "./types";

export const ALLOWED_IMAGE_EXT = ["jpg", "png"];

export const SOCIAL_DOMAINS: Record<string, string> = {
  "facebook.com": "Facebook",
  "instagram.com": "Instagram",
  "tiktok.com": "TikTok",
  "youtube.com": "YouTube",
  "youtu.be": "YouTube",
  "twitter.com": "Twitter / X",
  "x.com": "Twitter / X",
  "linkedin.com": "LinkedIn",
  "pinterest.com": "Pinterest",
};

// The social platforms a Karma EDM is expected to link.
export const EXPECTED_SOCIAL = ["Facebook", "Instagram", "TikTok", "YouTube"];

// True when an href is a Mailchimp merge tag, e.g. *|UNSUB|*.
export const isMergeTag = (href: string): boolean => /^\*\|.*\|\*$/.test(href.trim());

export const isHttps = (url: string): boolean => /^https:\/\//i.test(url.trim());

export const isLocalhost = (url: string): boolean =>
  /\/\/(localhost|127\.0\.0\.1)/i.test(url);

// Scheme-less / relative reference that isn't an anchor, mailto/tel, data URI,
// or a merge tag — i.e. something that likely won't resolve in an email.
export const isRelative = (url: string): boolean => {
  const u = url.trim();
  if (!u) return false;
  if (isMergeTag(u)) return false;
  if (/^(https?:)?\/\//i.test(u)) return false;
  if (/^(mailto:|tel:|#|data:)/i.test(u)) return false;
  return true;
};

// Lower-cased file extension of a URL, ignoring query/hash. "" if none.
export const extOf = (url: string): string => {
  const clean = url.split(/[?#]/)[0];
  const m = clean.match(/\.([a-z0-9]+)$/i);
  return m ? m[1].toLowerCase() : "";
};

// Platform name if the href points at a known social domain, else null.
export const socialPlatform = (href: string): string | null => {
  for (const domain in SOCIAL_DOMAINS) {
    if (href.includes(domain)) return SOCIAL_DOMAINS[domain];
  }
  return null;
};

export const getAnchors = (doc: Document): { text: string; href: string }[] =>
  Array.from(doc.querySelectorAll("a")).map((a) => ({
    text: (a.textContent ?? "").replace(/\s+/g, " ").trim(),
    href: a.getAttribute("href") ?? "",
  }));

export const getImages = (doc: Document): { src: string; alt: string }[] =>
  Array.from(doc.querySelectorAll("img")).map((img) => ({
    src: img.getAttribute("src") ?? "",
    alt: img.getAttribute("alt") ?? "",
  }));

// First anchor whose visible text matches `re`.
export const findAnchorByText = (
  doc: Document,
  re: RegExp,
): { text: string; href: string } | null =>
  getAnchors(doc).find((a) => re.test(a.text)) ?? null;

// All *|TOKEN|* merge tags in the raw HTML, deduped with counts. IF:/ELSE/END:
// tokens are classified as conditional.
export const extractVariables = (html: string): CheckVariable[] => {
  const byToken = new Map<string, CheckVariable>();
  const re = /\*\|([^|*]+)\|\*/g;
  let m: RegExpExecArray | null;
  while ((m = re.exec(html)) !== null) {
    const token = m[0];
    const name = m[1].trim();
    const kind: CheckVariable["kind"] = /^(IF:|ELSE|END:)/i.test(name)
      ? "conditional"
      : "merge";
    const existing = byToken.get(token);
    if (existing) existing.count += 1;
    else byToken.set(token, { token, name, count: 1, kind });
  }
  return Array.from(byToken.values());
};

// SendGrid / Handlebars substitution tags, e.g. {{full_name}} or {{#if x}}.
// Deduped with counts; block helpers ({{#…}} / {{/…}}) are conditional.
export const extractHandlebars = (html: string): CheckVariable[] => {
  const byToken = new Map<string, CheckVariable>();
  const re = /\{\{\s*([^{}]+?)\s*\}\}/g;
  let m: RegExpExecArray | null;
  while ((m = re.exec(html)) !== null) {
    const token = m[0];
    const name = m[1].trim();
    const kind: CheckVariable["kind"] = /^[#/^]/.test(name)
      ? "conditional"
      : "merge";
    const existing = byToken.get(token);
    if (existing) existing.count += 1;
    else byToken.set(token, { token, name, count: 1, kind });
  }
  return Array.from(byToken.values());
};

// Last path segment of a URL (filename), for display.
export const fileNameOf = (url: string): string => {
  const clean = url.split(/[?#]/)[0];
  const parts = clean.split("/");
  return parts[parts.length - 1] || clean;
};
