import type { InteractiveElement, PageContent } from "../../shared/types";
import type { ExtractMode } from "./context-builder";
import { normalizeComparable, normalizeUrlForMatch, getUrlPathSegments } from "./url-normalization";

function isVisible(element: InteractiveElement): boolean {
  return (
    element.visible !== false &&
    element.obscured !== true &&
    element.blockedByOverlay !== true
  );
}

function isInViewport(element: InteractiveElement): boolean {
  return element.inViewport !== false;
}

function looksLikeListingPage(page: PageContent): boolean {
  const haystack = normalizeComparable(
    [
      page.url,
      page.title,
      page.excerpt,
      page.content.slice(0, 2000),
      page.headings.map((heading) => heading.text).join(" "),
    ]
      .filter(Boolean)
      .join(" "),
  );

  return /\b(search|results|browse|discover|arrivals|new arrivals|staff picks|picks of the month|monthly picks|featured|bestsellers|best sellers|category|categories|books|book list|fiction|nonfiction|poetry|history|science|children)\b/.test(
    haystack,
  );
}

function isBlockedLabel(label: string): boolean {
  return /\b(home|menu|about|contact|privacy|terms|login|sign in|sign up|subscribe|newsletter|facebook|instagram|pinterest|share|print|next|previous|prev|sort|filter|wishlist|account|cart|checkout|view all|see all|refine|narrow|clear all|remove filter)\b/.test(
    label,
  );
}

function resultScore(
  page: PageContent,
  element: InteractiveElement,
  listingLike: boolean,
): number {
  if (element.type !== "link" || !element.href || !element.text?.trim()) return -1;

  const text = element.text.trim();
  const comparableText = normalizeComparable(text);
  const href = normalizeUrlForMatch(element.href);
  const pageUrl = normalizeUrlForMatch(page.url);
  const hrefSegments = getUrlPathSegments(element.href);
  const haystack = normalizeComparable(
    [text, element.description, element.selector, element.href, element.context]
      .filter(Boolean)
      .join(" "),
  );

  if (!comparableText || isBlockedLabel(comparableText)) return -1;

  let score = 0;

  if (element.context === "article") score += 5;
  if (element.context === "main" || element.context === "content") score += 4;
  if (!element.context) score += 1;

  if (isVisible(element)) score += 2;
  if (isInViewport(element)) score += 3;

  if (text.length >= 8 && text.length <= 140) score += 2;
  if (text.split(/\s+/).length >= 2) score += 2;
  if (text.split(/\s+/).length >= 4) score += 1;

  if (hrefSegments.length >= 2) score += 1;
  if (hrefSegments.some((segment) => /book|books|item|product|title|catalog/.test(segment))) {
    score += 3;
  }

  if (href && pageUrl) {
    try {
      if (new URL(href).origin === new URL(pageUrl).origin) score += 2;
    } catch {
      // ignore malformed URLs
    }
  }

  if (listingLike) score += 2;
  if (/\b(book|novel|story|poems|poetry|essays|memoir|history|science|fiction)\b/.test(haystack)) {
    score += 2;
  }

  if (/\b(author|hardcover|paperback|preorder|pre-order|signed edition)\b/.test(haystack)) {
    score += 1;
  }

  if (
    element.context === "nav" ||
    element.context === "header" ||
    element.context === "footer" ||
    element.context === "sidebar" ||
    element.context === "dialog"
  ) {
    score -= 6;
  }

  if (/\b(filter|sort|format|price|signed|staff picks|more results|view all)\b/.test(comparableText)) {
    score -= 3;
  }

  // Heavily penalize filter/condition/format links — these have URLs with
  // query parameters like ?condition=used or path segments like /format/paperback.
  // We can't block by text alone because "new", "good", "edition" etc. appear in
  // legitimate book titles. URL-based detection is much more reliable.
  try {
    const linkUrl = new URL(element.href);
    const filterParams = ["condition", "binding", "format", "availability", "sort", "filter", "price", "category_id", "view"];
    if (filterParams.some((p) => linkUrl.searchParams.has(p))) {
      score -= 10;
    }
    // Also catch path-based filter URLs (e.g. /format/paperback, /condition/used)
    const filterPathSegments = ["format", "condition", "binding", "availability", "sort"];
    const hasFilterPath = filterPathSegments.some((seg) =>
      hrefSegments.some((s) => s.toLowerCase() === seg)
    );
    if (hasFilterPath) {
      score -= 10;
    }
  } catch {
    // Not a valid URL — ignore
  }

  return score;
}

export function getCompactPrimaryResultLinks(
  page: PageContent,
  options?: { visibleOnly?: boolean; max?: number },
): InteractiveElement[] {
  const listingLike = looksLikeListingPage(page);
  const max = options?.max ?? 8;
  const visibleOnly = options?.visibleOnly === true;
  const seen = new Set<string>();

  return page.interactiveElements
    .filter((element) => !visibleOnly || isVisible(element))
    .map((element) => ({
      element,
      score: resultScore(page, element, listingLike),
    }))
    .filter(({ score }) => score >= (listingLike ? 5 : 7))
    .sort(
      (a, b) =>
        b.score - a.score ||
        (a.element.index ?? Number.MAX_SAFE_INTEGER) -
          (b.element.index ?? Number.MAX_SAFE_INTEGER),
    )
    .map(({ element }) => element)
    .filter((element) => {
      const key = `${normalizeComparable(element.text)}|${normalizeUrlForMatch(element.href) || ""}`;
      if (seen.has(key)) return false;
      seen.add(key);
      return true;
    })
    .slice(0, max);
}

/**
 * Quick heuristic: does this page look like a product detail page?
 * Product pages should never be forced into results_only mode because
 * the primary action is purchase (Add to Cart), not browsing results.
 */
function looksLikeProductPage(page: PageContent): boolean {
  const url = (page.url || "").toLowerCase();
  return (
    /\/(book|product|item|detail|dp|gp\/product)\//i.test(url) ||
    /\b(add to cart|add to bag|add to basket|buy now)\b/i.test(
      page.content.slice(0, 3000),
    )
  );
}

export function chooseCompactReadMode(
  page: PageContent,
  fallbackMode: ExtractMode,
): ExtractMode {
  if (fallbackMode === "results_only") return fallbackMode;
  // Never override to results_only on product detail pages —
  // the model needs to see purchase controls.
  if (looksLikeProductPage(page)) return fallbackMode;
  const candidates = getCompactPrimaryResultLinks(page, { max: 6 });
  if (candidates.length >= 4) return "results_only";
  if (candidates.length >= 2 && looksLikeListingPage(page)) return "results_only";
  return fallbackMode;
}
