import * as cheerio from "cheerio";
import type { FetchContext, NormalizedQuery, Offer, OfferAttribute, StoreAdapter } from "../core/types.js";
import { inStockFromText, lazyImageUrl, validPrice } from "./util.js";

// Autodoc.lv — the big pan-European catalog. Server-rendered results at
// /search?keyword=<q>. Its plain-HTTP session trick (homepage warm-up + Referer) died
// by M2 behind a Cloudflare managed challenge → onDemand (walled) store, fetched by
// navigating the resident headful browser (BrowserHub.fetchPageHtml).
// Live-verified 2026-07-12: the first navigation got the managed challenge (403), which
// cleared ITSELF in a few seconds (no human, no evasion — Cloudflare's own script in a
// real warm browser on a residential IP), issuing cf_clearance; the retry returned 200.
// noVNC solve remains the fallback if a cold session ever needs interaction.
//
// Spec risk note (§M4): Autodoc is the store most likely to tighten defenses. If this
// path starts challenging interactively every time, the sanctioned fallback is their
// affiliate feed — never an evasion arms race.
// Fixture: fixtures/autodoc/0986452041.html (recorded 2026-07-12).

const HOME = "https://www.autodoc.lv";

const SEARCH_URL = (q: string) => `${HOME}/search?keyword=${encodeURIComponent(q)}`;

const SELECTORS = {
  card: ".listing-item[data-article-id]", // carries data-price + data-generic-name
  titleLink: "a.listing-item__name",
  articleItem: ".listing-item__article-item", // "Preces numurs: 0 986 452 041" (may have siblings)
  brandImg: ".listing-item__image-brand img", // alt = "<BRAND> <generic name> <part number>"
  availability: ".listing-item__available",
  image: ".listing-item__image-product img",
  attrRow: ".product-description__item",
  attrLabel: ".product-description__item-title",
  attrValue: ".product-description__item-value",
};

export const autodoc: StoreAdapter = {
  id: "autodoc",
  displayName: "Autodoc.lv",
  storeHomepage: HOME,
  enabled: true,
  fetchMode: "onDemand",
  timeoutMs: 45000, // navigation + possible challenge auto-clear wait + one re-navigation

  async fetchRaw(q: NormalizedQuery, ctx: FetchContext): Promise<string> {
    // Walled: navigate the resident Chrome to the results page (see header comment).
    return ctx.browser.fetchPageHtml(this.id, SEARCH_URL(q.canonical), {
      waitForSelector: SELECTORS.card,
    });
  },

  parse(html: string): Offer[] {
    const $ = cheerio.load(html);
    const offers: Offer[] = [];

    $(SELECTORS.card).each((_, el) => {
      const card = $(el);

      const priceEur = Number(card.attr("data-price"));

      const articleText = card
        .find(SELECTORS.articleItem)
        .filter((_, a) => /Preces numurs:/.test($(a).text()))
        .first()
        .text();
      const partNumber = articleText.replace(/.*Preces numurs:\s*/s, "").trim();
      if (!partNumber || !validPrice(priceEur)) return;

      const link = card.find(SELECTORS.titleLink).first();
      const title = link.text().replace(/\s+/g, " ").trim();
      const url = link.attr("href") ?? "";

      // Brand image alt is "<BRAND> <generic name> <part number>" and the card knows its
      // generic name ("Eļļas filtrs") — the brand is everything before it.
      const genericName = (card.attr("data-generic-name") ?? "").trim();
      const brandAlt = (card.find(SELECTORS.brandImg).attr("alt") ?? "").trim();
      const brand =
        (genericName && brandAlt.includes(genericName)
          ? brandAlt.slice(0, brandAlt.indexOf(genericName)).trim()
          : "") || null;

      const attributes: OfferAttribute[] = [];
      card.find(SELECTORS.attrRow).each((_, row) => {
        const label = $(row).find(SELECTORS.attrLabel).text().replace(/\s+/g, " ").replace(/:\s*$/, "").trim();
        const value = $(row).find(SELECTORS.attrValue).text().replace(/\s+/g, " ").trim();
        if (label === "Preces numurs") return; // already surfaced as partNumber
        if (label && value && attributes.length < 8) attributes.push({ label, value });
      });

      const imageUrl = lazyImageUrl(card.find(SELECTORS.image).first(), "src");

      const availability = card.find(SELECTORS.availability).first().text().trim() || null;

      offers.push({
        store: autodoc.id,
        brand,
        partNumber,
        title,
        priceEur,
        inStock: inStockFromText(availability),
        availability,
        deliveryNote: null, // delivery date is filled client-side by JS; not in the markup
        url,
        imageUrl,
        attributes,
      });
    });

    return offers;
  },
};
