import axios from "axios";
import { PortalModel } from "../models/portalModel";
import { getProbeTarget } from "../config/portalProbeConfig";
import { findScraperInfo } from "../config/scraperRegistry";
import { PortalBrowserSession, isChallengePage } from "../scrapers/portalBrowserSession";
import { supportsScraperOperation } from "../scrapers/capabilities";

export interface ProbeResult {
  portal: string;
  slug: string;
  operation: "sale" | "rent";
  url: string;
  httpStatus: number | null;
  title: string;
  selector: string;
  matchCount: number;
  htmlLength: number;
  challenge: boolean;
  ok: boolean;
  diagnosis: string;
  durationMs: number;
  engine: string;
}

function diagnoseProbe(r: Omit<ProbeResult, "diagnosis" | "ok">): { ok: boolean; diagnosis: string } {
  if (r.challenge) return { ok: false, diagnosis: "Bloqueo anti-bot / challenge detectado" };
  if (r.httpStatus && r.httpStatus >= 400) return { ok: false, diagnosis: `HTTP ${r.httpStatus}` };
  if (r.matchCount >= 5) return { ok: true, diagnosis: `OK — ${r.matchCount} coincidencias con selector` };
  if (r.matchCount > 0) return { ok: false, diagnosis: `Pocos resultados (${r.matchCount}) — posible hidratación incompleta o selector obsoleto` };
  if (r.htmlLength < 3000) return { ok: false, diagnosis: "HTML muy corto — página vacía o redirección" };
  return { ok: false, diagnosis: "Sin coincidencias — revisar selector o bloqueo silencioso" };
}

async function probeWithPlaywright(url: string, selector: string, portalName: string): Promise<{
  httpStatus: number | null;
  title: string;
  matchCount: number;
  htmlLength: number;
  challenge: boolean;
  finalUrl: string;
}> {
  const session = new PortalBrowserSession(portalName, 45000);
  try {
    const html = await session.fetchHtml(url, { waitForSelector: selector.split(",")[0].trim() });
    const titleMatch = html.match(/<title[^>]*>([^<]*)<\/title>/i);
    const title = titleMatch ? titleMatch[1].trim() : "";
    let count = 0;
    if (selector.includes("article")) count = (html.match(/<article[\s>]/gi) || []).length;
    else if (selector.includes("a[href")) count = (html.match(/href="/gi) || []).length;
    else count = (html.match(/class="/gi) || []).length;
    const challenge = isChallengePage({ url, title, html });
    return { httpStatus: 200, title, matchCount: count, htmlLength: html.length, challenge, finalUrl: url };
  } finally {
    await session.close();
  }
}

async function probeWithHttp(url: string, selector: string): Promise<{
  httpStatus: number | null;
  title: string;
  matchCount: number;
  htmlLength: number;
  challenge: boolean;
  finalUrl: string;
}> {
  const res = await axios.get(url, {
    timeout: 20000,
    maxRedirects: 5,
    validateStatus: () => true,
    headers: {
      "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
      "Accept-Language": "es-ES,es;q=0.9",
    },
  });
  const html = String(res.data || "");
  const titleMatch = html.match(/<title[^>]*>([^<]*)<\/title>/i);
  const title = titleMatch ? titleMatch[1].trim() : "";
  let count = 0;
  if (selector.includes("article")) count = (html.match(/<article[\s>]/gi) || []).length;
  else if (selector.includes("inmueble")) count = (html.match(/\/inmueble\//gi) || []).length;
  else if (selector.includes("listing")) count = (html.match(/listing/gi) || []).length;
  else count = (html.match(/class="/gi) || []).length;
  const challenge = isChallengePage({ status: res.status, url: res.request?.res?.responseUrl || url, title, html });
  return {
    httpStatus: res.status,
    title,
    matchCount: count,
    htmlLength: html.length,
    challenge,
    finalUrl: url,
  };
}

export async function probePortal(slug: string, operation: "sale" | "rent" = "sale"): Promise<ProbeResult> {
  const start = Date.now();
  const portal = await PortalModel.findBySlug(slug);
  if (!portal) throw new Error(`Portal '${slug}' no encontrado`);

  if (!supportsScraperOperation(portal.scraper_class, operation)) {
    return {
      portal: portal.name,
      slug,
      operation,
      url: "",
      httpStatus: null,
      title: "",
      selector: "",
      matchCount: 0,
      htmlLength: 0,
      challenge: false,
      ok: false,
      diagnosis: `Operación '${operation}' no soportada por ${portal.scraper_class}`,
      durationMs: Date.now() - start,
      engine: "n/a",
    };
  }

  const target = getProbeTarget(slug);
  const scraperInfo = findScraperInfo(portal.scraper_class);
  const engine = target?.engine || scraperInfo?.engine || "cheerio";
  const url = operation === "rent" && target?.rentUrl ? target.rentUrl : target?.saleUrl;
  const selector = target?.selector || "article, .listing, a[href*='inmueble']";

  if (!url) {
    return {
      portal: portal.name,
      slug,
      operation,
      url: "",
      httpStatus: null,
      title: "",
      selector,
      matchCount: 0,
      htmlLength: 0,
      challenge: false,
      ok: false,
      diagnosis: "Sin URL de prueba configurada para este portal",
      durationMs: Date.now() - start,
      engine,
    };
  }

  const raw = engine === "playwright"
    ? await probeWithPlaywright(url, selector, portal.name)
    : await probeWithHttp(url, selector);

  const partial: Omit<ProbeResult, "diagnosis" | "ok"> = {
    portal: portal.name,
    slug,
    operation,
    url: raw.finalUrl || url,
    httpStatus: raw.httpStatus,
    title: raw.title.slice(0, 120),
    selector,
    matchCount: raw.matchCount,
    htmlLength: raw.htmlLength,
    challenge: raw.challenge,
    durationMs: Date.now() - start,
    engine,
  };
  const { ok, diagnosis } = diagnoseProbe(partial);
  return { ...partial, ok, diagnosis };
}

export async function probeAllPortals(operation: "sale" | "rent" = "sale"): Promise<ProbeResult[]> {
  const portals = await PortalModel.findAll();
  const results: ProbeResult[] = [];
  for (const p of portals) {
    try {
      results.push(await probePortal(p.slug, operation));
    } catch (e) {
      results.push({
        portal: p.name,
        slug: p.slug,
        operation,
        url: "",
        httpStatus: null,
        title: "",
        selector: "",
        matchCount: 0,
        htmlLength: 0,
        challenge: false,
        ok: false,
        diagnosis: (e as Error).message,
        durationMs: 0,
        engine: "error",
      });
    }
  }
  return results;
}
