import * as cheerio from "cheerio";
import { parsePriceEur } from "../core/normalize.js";
import type { FetchContext, NormalizedQuery, Offer, OfferAttribute, StoreAdapter } from "../core/types.js";

// PrestaShop + custom "carmodsearch" module. Two-step search:
//   POST /carparts/search/ (CarModAjax=Y…ArtSearch=<q>)  -> body "REDIRECT:/carparts/search/<q>/"
//   GET  that pretty URL                                 -> the rendered results page
// Prices are inline as data-ga-price on the add-to-cart button (no secondary call needed).
// robots.txt checked 2026-07-10: /carparts/search not disallowed.
// Fixture: fixtures/xparts/W71252.html (recorded 2026-07-10).

const BASE = "https://www.xparts.lv";
const SEARCH_POST = `${BASE}/carparts/search/`;
const SEARCH_FORM = (q: string): Record<string, string> => ({
  CarModAjax: "Y",
  ShortResult: "Y",
  HideStat: "Y",
  WithRedirects: "Y",
  ArtSearch: q,
});

const SELECTORS = {
  card: ".CmInnerBlockList",
  info: ".info_bl[data-artnum]", // data-artnum, data-brand, data-link
  name: ".CmListName",
  addToCart: ".CmAddToCart[data-ga-price]", // data-ga-price (clean decimal), data-ga-name
  inStock: ".cm_InStock",
  image: "img.CmProdIm", // absolute URL, present on every card in the fixture
  // Spec rows are printed right on the listing. Only .CmPropsInnerBlock holds the live
  // markup — the same rows are repeated in HTML comments, which cheerio doesn't parse.
  attrRow: ".CmPropsListItem .CmPropsInnerBlock",
  attrLabel: ".CmCriName",
  // Parent of the .CmCriValue span(s) — multi-value rows ("93 mm, 92 mm") keep the
  // ", " separator as a text node between the spans, so read the whole container.
  attrValue: ".CmPropVal",
  // .CmTimeDelivery carries only data-suplstock (internal warehouse codes like "APN"),
  // no user-meaningful delivery time — so deliveryNote stays null for this store.
};

export const xparts: StoreAdapter = {
  id: "xparts",
  displayName: "XPARTS.lv",
  storeHomepage: "https://www.xparts.lv",
  enabled: true,
  // Two HTTP steps + the 2s politeness gap between them + a ~1.2MB results page:
  // needs more than the 8s default budget.
  timeoutMs: 15000,

  async fetchRaw(q: NormalizedQuery, ctx: FetchContext): Promise<string> {
    const body = await ctx.http.postForm(this.id, SEARCH_POST, SEARCH_FORM(q.canonical), {
      headers: { "X-Requested-With": "XMLHttpRequest", Referer: BASE + "/" },
    });
    const m = body.match(/REDIRECT:(\S+)/);
    const path = m?.[1] ?? `/carparts/search/${q.canonical.toLowerCase()}/`;
    return ctx.http.get(this.id, BASE + path);
  },

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

    $(SELECTORS.card).each((_, el) => {
      const card = $(el);
      const info = card.find(SELECTORS.info).first();
      const cart = card.find(SELECTORS.addToCart).first();

      const link = info.attr("data-link") ?? "";
      const url = link ? encodeURI(BASE + link) : "";

      const attributes: OfferAttribute[] = [];
      card.find(SELECTORS.attrRow).each((_, row) => {
        const label = $(row).find(SELECTORS.attrLabel).text().trim().replace(/:$/, "");
        const value = $(row).find(SELECTORS.attrValue).text().replace(/\s+/g, " ").trim();
        if (!label && !value) return;
        // Flag-style rows ("Ar atgriezējvārstu") have the text in the label slot and no value.
        if (value) attributes.push({ label, value });
        else attributes.push({ label: "", value: label });
      });

      offers.push({
        store: xparts.id,
        brand: info.attr("data-brand")?.trim() || null,
        partNumber: (info.attr("data-artnum") ?? cart.attr("data-ga-id") ?? "").trim(),
        title: card.find(SELECTORS.name).first().text().trim() || cart.attr("data-ga-name") || "",
        priceEur: parsePriceEur(cart.attr("data-ga-price") ?? ""),
        // Only the in-stock marker exists; a card without it may be orderable, so "unknown".
        inStock: card.find(SELECTORS.inStock).length > 0 ? true : null,
        availability: card.find(SELECTORS.inStock).length > 0 ? "Pieejams" : null,
        deliveryNote: null,
        url,
        imageUrl: card.find(SELECTORS.image).first().attr("src") ?? null,
        attributes,
      });
    });

    return offers;
  },
};
