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

// Carparts.lv's exact-article API does not include OE-number matches and their
// analogues. The public /meklet route does, so use the same search surface as
// the storefront and parse its fully streamed HTML response.
const SITE = "https://carparts.lv";
const SEARCH = (code: string): string => `${SITE}/meklet?s=${encodeURIComponent(code)}`;

interface CarpartsArticle {
  articleNumber?: string;
  genericArticleDescription?: string;
  mfrName?: string;
  thumb?: string;
  slug?: string;
  price?: string | number;
  stock?: number;
}

function parseLegacyJson(raw: string): Offer[] {
  const articles = JSON.parse(raw) as CarpartsArticle[];
  if (!Array.isArray(articles)) return [];

  return articles.flatMap((article): Offer[] => {
    const priceEur =
      typeof article.price === "number" ? article.price : parsePriceEur(article.price ?? "");
    if (!validPrice(priceEur)) return [];

    const inStock = typeof article.stock === "number" && article.stock > 0;
    return [
      {
        store: carparts.id,
        brand: article.mfrName?.trim() || null,
        partNumber: (article.articleNumber ?? "").trim(),
        title: article.genericArticleDescription?.trim() || "",
        priceEur,
        inStock: typeof article.stock === "number" ? inStock : null,
        availability: inStock ? `Pieejams (${article.stock} gab.)` : null,
        deliveryNote: null,
        url: article.slug ? `${SITE}/produkts/${article.slug}` : SITE,
        imageUrl: article.thumb?.trim() || null,
        attributes: [],
      },
    ];
  });
}

function normalizeText(value: string): string {
  return value.replace(/\s+/g, " ").trim();
}

function imageUrl(raw: string | undefined): string | null {
  if (!raw) return null;

  try {
    const absolute = new URL(raw, SITE);
    if (absolute.pathname === "/_next/image") {
      const original = absolute.searchParams.get("url");
      return original ? new URL(original, SITE).toString() : null;
    }
    return absolute.toString();
  } catch {
    return null;
  }
}

function parseSearchHtml(raw: string, q: NormalizedQuery): Offer[] {
  const $ = cheerio.load(raw);
  const offers: Offer[] = [];
  const seen = new Set<string>();

  $('a[href^="/produkts/"]:has(h2)').each((_index, element) => {
    const link = $(element);
    const href = link.attr("href");
    if (!href || seen.has(href)) return;

    const card = link.closest("div.flex.flex-col");
    if (!card.length) return;

    const manufacturerRow = card
      .find("div.text-gray-600.text-sm")
      .filter((_i, row) => normalizeText($(row).text()).startsWith("Ražotājs:"))
      .first();
    const brand = normalizeText(manufacturerRow.find("span.font-semibold").first().text());
    const heading = normalizeText(link.find("h2").first().text());
    const brandNeedle = ` ${brand}`;
    const brandOffset = brand ? heading.lastIndexOf(brandNeedle) : -1;
    if (!brand || brandOffset < 0) return;

    const title = heading.slice(0, brandOffset).trim();
    const renderedPartNumber = heading.slice(brandOffset + brandNeedle.length).trim();
    const partNumber = renderedPartNumber || q.canonical;
    const priceEur = parsePriceEur(card.find("p.text-xl.font-bold").first().text());
    if (!title || !partNumber || !validPrice(priceEur)) return;

    const stockText = normalizeText(
      card
        .find("span.text-sm.text-gray-400")
        .filter((_i, row) => /^(?:Pieejams:|Nav pieejams)/i.test(normalizeText($(row).text())))
        .first()
        .text(),
    );
    const stockMatch = stockText.match(/^Pieejams:\s*(\d+)/i);
    const stock = stockMatch ? Number(stockMatch[1]) : null;
    const inStock = stock !== null ? stock > 0 : /^Nav pieejams/i.test(stockText) ? false : null;

    const attributes: OfferAttribute[] = [];
    card.find("div.text-gray-600.text-sm").each((_i, row) => {
      if (attributes.length >= 8) return;
      const value = normalizeText($(row).find("span.font-semibold").first().text());
      const fullText = normalizeText($(row).text());
      if (!value || fullText.startsWith("Ražotājs:") || fullText.startsWith("EAN:")) return;
      const valueOffset = fullText.lastIndexOf(value);
      const label = normalizeText(fullText.slice(0, valueOffset)).replace(/:\s*$/, "");
      if (label) attributes.push({ label, value });
    });

    const images = card.find("img");
    const image =
      images
        .filter((_i, img) => /digital-assets\.tecalliance\.services/.test($(img).attr("src") ?? ""))
        .first()
        .attr("src") ??
      images
        .filter((_i, img) => normalizeText($(img).attr("alt") ?? "") === title)
        .first()
        .attr("src");

    seen.add(href);
    offers.push({
      store: carparts.id,
      brand,
      partNumber,
      title,
      priceEur,
      inStock,
      availability: stock !== null && stock > 0 ? `Pieejams (${stock} gab.)` : null,
      deliveryNote: null,
      url: new URL(href, SITE).toString(),
      imageUrl: imageUrl(image),
      attributes,
    });
  });

  return offers;
}

export const carparts: StoreAdapter = {
  id: "carparts",
  displayName: "Carparts.lv",
  storeHomepage: SITE,
  enabled: true,
  timeoutMs: 30000,
  fixtureExt: "html",

  async fetchRaw(q: NormalizedQuery, ctx: FetchContext): Promise<string> {
    return ctx.http.get(this.id, SEARCH(q.canonical), { headers: { Accept: "text/html" } });
  },

  parse(raw: string, q: NormalizedQuery): Offer[] {
    return raw.trimStart().startsWith("[") ? parseLegacyJson(raw) : parseSearchHtml(raw, q);
  },
};
