import { detectViewportConfiguration } from "../../mobile-first/viewport-detector";
import { analyzeTouchTargets } from "../../mobile-first/touch-target-analyzer";
import { detectMobileSpecificSchema } from "../../mobile-first/mobile-schema-detector";
import { detectAmpStatus } from "../../mobile-first/amp-detector";
import { analyzeMobileAboveFoldComposition } from "../../mobile-first/mobile-composition-analyzer";
import { scorePerPageMobile, buildTopPriorityFindings } from "../../mobile-first/mobile-scorer";
import type {
  MobileFirstAudit,
  PerPageMobileEvaluation,
  MobileFinding,
  MobileDimension,
} from "../../mobile-first/types";
import type { LiveVisualAnnotations } from "../types";

const MAX_PAGES = 5;
const SECTION_TIMEOUT_MS = 60_000;
const PAGE_FETCH_TIMEOUT_MS = 10_000;

async function fetchPageHtml(url: string): Promise<string> {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), PAGE_FETCH_TIMEOUT_MS);
  try {
    const res = await fetch(url, {
      signal: controller.signal,
      headers: { "User-Agent": "Mozilla/5.0 (compatible; audit-bot/1.0)" },
    });
    clearTimeout(timer);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.text();
  } catch (err) {
    clearTimeout(timer);
    throw err;
  }
}

function classifyPageLabel(url: string): string {
  const path = url.replace(/^https?:\/\/[^/]+/, "").toLowerCase();
  if (path === "/" || path === "") return "Homepage";
  if (/\/contact/i.test(path)) return "Contact Page";
  if (/\/(services?|treatments?|solutions?)/i.test(path)) return "Services Page";
  if (/\/about/i.test(path)) return "About Page";
  if (/\/(blog|news)/i.test(path)) return "Blog Page";
  if (/\/(products?|shop)/i.test(path)) return "Product Page";
  return "Page";
}

function buildFindings(
  evaluation: Omit<PerPageMobileEvaluation, "findings" | "mobileScore">,
): MobileFinding[] {
  const findings: MobileFinding[] = [];
  let idx = 0;

  // Viewport findings
  for (const issue of evaluation.viewport.issues) {
    findings.push({
      id: `vp-${idx++}`,
      dimension: "viewport" as MobileDimension,
      severity: evaluation.viewport.hasViewportMeta ? "high" : "critical",
      title: evaluation.viewport.hasViewportMeta
        ? "Viewport configuration issue"
        : "Missing viewport meta tag",
      detail: issue,
      affectedUrl: evaluation.pageUrl,
      recommendation: evaluation.viewport.hasViewportMeta
        ? "Update viewport meta tag to use width=device-width, initial-scale=1"
        : "Add <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> to the page head",
    });
  }

  // Touch target findings
  for (const issue of evaluation.touchTargets.issues) {
    findings.push({
      id: `tt-${idx++}`,
      dimension: "touch_targets" as MobileDimension,
      severity: evaluation.touchTargets.passRate < 60 ? "high" : "moderate",
      title: "Touch target size or spacing issue",
      detail: issue,
      affectedUrl: evaluation.pageUrl,
      recommendation:
        "Ensure all interactive elements are at least 44px in height and width with adequate spacing between them",
    });
  }

  // Mobile schema findings
  if (!evaluation.mobileSchema.hasMobileSpecificSchema) {
    findings.push({
      id: `ms-${idx++}`,
      dimension: "mobile_schema" as MobileDimension,
      severity: "moderate",
      title: "Mobile-relevant structured data missing",
      detail:
        evaluation.mobileSchema.detectedTypes.length === 0
          ? "No structured data detected on this page"
          : `Detected types (${evaluation.mobileSchema.detectedTypes.join(", ")}) do not include mobile-priority schema`,
      affectedUrl: evaluation.pageUrl,
      recommendation: `Add structured data for: ${evaluation.mobileSchema.missingRecommended.slice(0, 2).join(", ")}`,
    });
  }

  // AMP findings
  if (evaluation.ampStatus.hasAmpVersion && evaluation.ampStatus.ampValid === false) {
    findings.push({
      id: `amp-${idx++}`,
      dimension: "amp" as MobileDimension,
      severity: "moderate",
      title: "AMP version detected but may be invalid",
      detail: `AMP URL ${evaluation.ampStatus.ampUrl ?? "unknown"} did not return valid AMP markup`,
      affectedUrl: evaluation.pageUrl,
      recommendation:
        "Validate the AMP page passes AMP validation and includes the required AMP boilerplate",
    });
  }

  // Above-fold composition findings
  for (const note of evaluation.aboveFoldComposition.verticalNotes) {
    findings.push({
      id: `af-${idx++}`,
      dimension: "above_fold_composition" as MobileDimension,
      severity: "moderate",
      title: "Mobile above-fold conversion element absent",
      detail: note,
      affectedUrl: evaluation.pageUrl,
      recommendation:
        "Prioritize conversion-critical elements so they appear without scrolling on mobile devices",
    });
  }

  return findings;
}

async function evaluatePage(
  pageUrl: string,
  mobileScreenshotUrl: string | null,
  vertical: string,
): Promise<PerPageMobileEvaluation> {
  const pageLabel = classifyPageLabel(pageUrl);
  let domHtml = "";

  try {
    domHtml = await fetchPageHtml(pageUrl);
  } catch {
    // Return a degraded evaluation if the page can't be fetched
    const empty: Omit<PerPageMobileEvaluation, "findings" | "mobileScore"> = {
      pageUrl,
      pageLabel,
      mobileScreenshotUrl,
      viewport: { hasViewportMeta: false, viewportContent: null, isOptimal: false, issues: [] },
      touchTargets: { totalInteractiveElements: 0, tooSmallCount: 0, tooCloseCount: 0, passRate: 100, issues: [] },
      mobileSchema: { hasLocalBusinessSchema: false, hasMobileSpecificSchema: false, detectedTypes: [], missingRecommended: [] },
      ampStatus: { hasAmpVersion: false, ampUrl: null, ampValid: null },
      aboveFoldComposition: { hasCta: false, hasPhoneNumber: false, hasHours: false, hasPrice: false, hasDemo: false, score: 0, verticalNotes: [] },
    };
    return { ...empty, findings: [], mobileScore: 0 };
  }

  const [viewport, touchTargets, mobileSchema, ampStatus] = await Promise.allSettled([
    Promise.resolve(detectViewportConfiguration(domHtml)),
    Promise.resolve(analyzeTouchTargets(domHtml)),
    Promise.resolve(detectMobileSpecificSchema(domHtml)),
    detectAmpStatus(domHtml, pageUrl),
  ]);

  const evalBase: Omit<PerPageMobileEvaluation, "findings" | "mobileScore"> = {
    pageUrl,
    pageLabel,
    mobileScreenshotUrl,
    viewport:
      viewport.status === "fulfilled"
        ? viewport.value
        : { hasViewportMeta: false, viewportContent: null, isOptimal: false, issues: [] },
    touchTargets:
      touchTargets.status === "fulfilled"
        ? touchTargets.value
        : { totalInteractiveElements: 0, tooSmallCount: 0, tooCloseCount: 0, passRate: 100, issues: [] },
    mobileSchema:
      mobileSchema.status === "fulfilled"
        ? mobileSchema.value
        : { hasLocalBusinessSchema: false, hasMobileSpecificSchema: false, detectedTypes: [], missingRecommended: [] },
    ampStatus:
      ampStatus.status === "fulfilled"
        ? ampStatus.value
        : { hasAmpVersion: false, ampUrl: null, ampValid: null },
    aboveFoldComposition: analyzeMobileAboveFoldComposition(domHtml, vertical),
  };

  const findings = buildFindings(evalBase);
  const mobileScore = scorePerPageMobile({ ...evalBase, findings, mobileScore: 0 }, vertical);

  return { ...evalBase, findings, mobileScore };
}

export async function aggregateMobileFirstAudit({
  brandDomain,
  vertical,
  liveVisualAnnotations,
}: {
  brandDomain: string;
  vertical: string;
  liveVisualAnnotations: LiveVisualAnnotations | null | undefined;
}): Promise<MobileFirstAudit | null> {
  const start = Date.now();

  // Reuse Phase 12 page list; fall back to homepage only
  const pageList: Array<{ url: string; mobileScreenshotUrl: string | null }> =
    liveVisualAnnotations?.perPageAnnotations.length
      ? liveVisualAnnotations.perPageAnnotations
          .slice(0, MAX_PAGES)
          .map((p) => ({ url: p.pageUrl, mobileScreenshotUrl: p.mobileScreenshotUrl }))
      : [{ url: brandDomain, mobileScreenshotUrl: null }];

  const pagePromises = pageList.map(({ url, mobileScreenshotUrl }) =>
    evaluatePage(url, mobileScreenshotUrl, vertical),
  );

  const timeoutPromise = new Promise<null>((resolve) =>
    setTimeout(() => resolve(null), SECTION_TIMEOUT_MS),
  );

  const settled = await Promise.race([
    Promise.allSettled(pagePromises),
    timeoutPromise,
  ]);

  if (settled === null) {
    console.warn("[mobile-first-audit] Section timeout reached");
    return null;
  }

  const perPageEvaluations: PerPageMobileEvaluation[] = settled
    .filter(
      (r): r is PromiseFulfilledResult<PerPageMobileEvaluation> => r.status === "fulfilled",
    )
    .map((r) => r.value);

  if (perPageEvaluations.length === 0) return null;

  const overallMobileScore =
    perPageEvaluations.length > 0
      ? Math.round(
          perPageEvaluations.reduce((s, e) => s + e.mobileScore, 0) /
            perPageEvaluations.length,
        )
      : 0;

  const scoreTier: MobileFirstAudit["scoreTier"] =
    overallMobileScore >= 85
      ? "excellent"
      : overallMobileScore >= 65
        ? "good"
        : overallMobileScore >= 45
          ? "needs_improvement"
          : "poor";

  const dimensions: MobileDimension[] = [
    "viewport",
    "touch_targets",
    "mobile_schema",
    "amp",
    "above_fold_composition",
  ];

  const dimensionBreakdown = Object.fromEntries(
    dimensions.map((dim) => {
      const dimFindings = perPageEvaluations.flatMap((e) =>
        e.findings.filter((f) => f.dimension === dim),
      );
      return [
        dim,
        {
          avgScore: 0,
          findingCount: dimFindings.length,
          criticalCount: dimFindings.filter((f) => f.severity === "critical").length,
        },
      ];
    }),
  ) as MobileFirstAudit["dimensionBreakdown"];

  const topPriorityFindings = buildTopPriorityFindings(perPageEvaluations);

  const verticalSpecificNotes = perPageEvaluations
    .flatMap((e) => e.aboveFoldComposition.verticalNotes)
    .filter((v, i, arr) => arr.indexOf(v) === i)
    .slice(0, 3);

  return {
    hasAudit: true,
    windowDate: new Date().toISOString(),
    pagesEvaluated: perPageEvaluations.length,
    overallMobileScore,
    scoreTier,
    perPageEvaluations,
    topPriorityFindings,
    dimensionBreakdown,
    verticalSpecificNotes,
    patentRelevance:
      "Mobile usability directly affects crawlability and conversion probability for mobile-first visitors.",
    costMetrics: {
      pagesAnalyzed: perPageEvaluations.length,
      runtimeSeconds: Math.round((Date.now() - start) / 1000),
      estimatedCostUsd: 0,
    },
  };
}
