/**
 * Page Strategic Pass — Per-Page Scoring Service
 * ─────────────────────────────────────────────────
 * Selects high-value pages and computes a composite health score for each.
 * All computation is deterministic from crawl + content + behavioral data.
 *
 * Internal module — no third-party vendor names in user-facing output.
 */

import type { PageData, BehavioralContext, AiAttributionContext } from "../types";
import type { ContentSample } from "../scrapers/firecrawl-content";
import type { PageStrategicPass, PageHealthCard } from "../types";

// ─── Page health scoring weights (must sum to 1.0) ────────────────────────────

const WEIGHTS = {
  schema: 0.20,
  h1: 0.15,
  contentDepth: 0.20,
  cta: 0.15,
  internalLinks: 0.10,
  cwv: 0.10,
  aiCitationHeuristic: 0.10,
};

// ─── URL classification helpers ───────────────────────────────────────────────

function classifyPageType(url: string): PageHealthCard["pageType"] {
  const path = url.toLowerCase().replace(/^https?:\/\/[^/]+/, "");
  if (path === "/" || path === "" || /^\/(index\.html?)?$/.test(path)) return "homepage";
  if (/\/contact(?:-us)?/i.test(path)) return "contact";
  if (/\/(services?|treatments?|procedures?|solutions?|offerings?)/i.test(path)) return "service";
  if (/\/(products?|shop|store|catalog)/i.test(path)) return "product";
  if (/\/(about(?:-us)?|team|story|mission)/i.test(path)) return "about";
  if (/\/(blog|news|articles?|posts?|insights?|resources?)/i.test(path)) return "blog";
  if (/\/(pricing|plans?|packages?)/i.test(path)) return "pricing";
  if (/\/(faq|help|support|knowledge-base)/i.test(path)) return "faq";
  return "other";
}

function isHomepage(url: string): boolean {
  const path = url.replace(/^https?:\/\/[^/]+/, "").replace(/\/$/, "");
  return path === "" || path === "/index.html" || path === "/index.htm";
}

function isContactPage(url: string): boolean {
  return /\/contact(?:-us)?/i.test(url);
}

// ─── Page scoring ─────────────────────────────────────────────────────────────

function scoreSchema(page: PageData): number {
  if (page.hasSchema && page.schemaTypes.length > 0) return 100;
  if (page.hasSchema) return 40; // present but no types detected — likely malformed
  return 0;
}

function scoreH1(page: PageData): number {
  if (!page.h1) return 0;
  if (page.h1.length < 15) return 40;
  if (page.h1.length > 100) return 70;
  return 100;
}

function scoreContentDepth(page: PageData, sample: ContentSample | undefined): number {
  const wc = page.wordCount ?? sample?.wordCount ?? 0;
  if (wc >= 600) return 100;
  if (wc >= 300) return 65;
  if (wc >= 150) return 35;
  return 0;
}

function scoreCTA(sample: ContentSample | undefined): number {
  if (!sample) return 50; // no content data — neutral
  if (sample.hasCTA && sample.ctaExamples.length >= 2) return 100;
  if (sample.hasCTA) return 70;
  if (sample.hasPhoneClick) return 60;
  return 10;
}

function scoreInternalLinks(page: PageData): number {
  const count = page.internalLinks.length;
  if (count >= 5) return 100;
  if (count >= 2) return 65;
  if (count >= 1) return 35;
  return 0;
}

function scoreCWV(url: string, behavioralData: BehavioralContext | null): number {
  if (!behavioralData) return 50; // no data — neutral
  // CWV scoring uses behavioral proxy: pages with high session count + low bounce = healthy UX
  const pageData = behavioralData.pages.find((p) => p.url === url || normUrlSimple(p.url) === normUrlSimple(url));
  if (!pageData) return 50;
  const bounce = pageData.bounceRate;
  if (bounce === null) return 50;
  if (bounce < 0.35) return 100;
  if (bounce < 0.55) return 65;
  if (bounce < 0.75) return 30;
  return 0;
}

function scoreAiCitationHeuristic(page: PageData, sample: ContentSample | undefined): number {
  let score = 0;
  if (page.hasSchema && page.schemaTypes.length > 0) score += 30;
  if (sample?.hasFAQ) score += 25;
  if (page.wordCount && page.wordCount >= 400) score += 25;
  if (sample?.hasLocationSchema) score += 20;
  return Math.min(100, score);
}

function normUrlSimple(url: string): string {
  return url.toLowerCase().replace(/^https?:\/\//, "").replace(/\/$/, "");
}

function computePageHealthScore(
  page: PageData,
  sample: ContentSample | undefined,
  behavioralData: BehavioralContext | null,
): number {
  const raw =
    scoreSchema(page) * WEIGHTS.schema +
    scoreH1(page) * WEIGHTS.h1 +
    scoreContentDepth(page, sample) * WEIGHTS.contentDepth +
    scoreCTA(sample) * WEIGHTS.cta +
    scoreInternalLinks(page) * WEIGHTS.internalLinks +
    scoreCWV(page.url, behavioralData) * WEIGHTS.cwv +
    scoreAiCitationHeuristic(page, sample) * WEIGHTS.aiCitationHeuristic;
  return Math.round(raw);
}

// ─── Issue detection ──────────────────────────────────────────────────────────

interface PageIssue {
  severity: "critical" | "high" | "medium" | "low";
  issue: string;
  fix: string;
}

function detectPageIssues(
  page: PageData,
  sample: ContentSample | undefined,
  behavioralData: BehavioralContext | null,
): PageIssue[] {
  const issues: PageIssue[] = [];

  if (!page.h1) {
    issues.push({ severity: "critical", issue: "Missing H1 heading", fix: "Add a descriptive H1 that matches the page's primary search intent" });
  } else if (page.h1.length < 15) {
    issues.push({ severity: "high", issue: "H1 too short", fix: `Expand H1 to clearly describe the page topic (currently: "${page.h1}")` });
  }

  if (!page.hasSchema) {
    issues.push({ severity: "high", issue: "No structured data markup", fix: "Add relevant JSON-LD schema block (Service, LocalBusiness, or FAQPage as appropriate)" });
  }

  const wc = page.wordCount ?? sample?.wordCount ?? 0;
  if (wc < 150) {
    issues.push({ severity: "critical", issue: `Thin content (${wc} words)`, fix: "Expand page content to at least 400 words with clear service descriptions and FAQs" });
  } else if (wc < 300) {
    issues.push({ severity: "high", issue: `Low content depth (${wc} words)`, fix: "Expand content to 400+ words, add FAQ section and supporting detail paragraphs" });
  }

  if (sample && !sample.hasCTA && !sample.hasPhoneClick) {
    issues.push({ severity: "high", issue: "No clear call-to-action detected", fix: "Add a prominent CTA (button or phone link) above the fold and after key content sections" });
  }

  if (!page.metaDescription) {
    issues.push({ severity: "medium", issue: "Missing meta description", fix: "Add a 130-155 character meta description with primary keyword and CTA" });
  } else if (page.metaDescription.length > 160) {
    issues.push({ severity: "low", issue: "Meta description too long", fix: `Trim to 155 characters (currently ${page.metaDescription.length} chars)` });
  }

  if (page.internalLinks.length === 0) {
    issues.push({ severity: "medium", issue: "No outbound internal links", fix: "Add 2-3 internal links to related service pages or the contact page" });
  }

  const behavPage = behavioralData?.pages.find((p) => normUrlSimple(p.url) === normUrlSimple(page.url));
  if (behavPage?.bounceRate !== null && behavPage?.bounceRate !== undefined && behavPage.bounceRate > 0.75) {
    issues.push({ severity: "high", issue: `High bounce rate (${Math.round(behavPage.bounceRate * 100)}%)`, fix: "Improve page load speed, add engaging above-fold content, and ensure CTA is visible without scrolling" });
  }

  return issues.sort((a, b) => {
    const order = { critical: 0, high: 1, medium: 2, low: 3 };
    return order[a.severity] - order[b.severity];
  });
}

// ─── Top recommendation ───────────────────────────────────────────────────────

function buildTopRecommendation(issues: PageIssue[], page: PageData, sample: ContentSample | undefined): string {
  if (issues.length === 0) return "Monitor performance and ensure schema stays valid as content updates are made.";
  const top = issues[0]!;
  return top.fix;
}

// ─── Cross-references ─────────────────────────────────────────────────────────

function buildCrossReferences(page: PageData): Array<{ sectionId: string; label: string }> {
  const refs: Array<{ sectionId: string; label: string }> = [];
  if (!page.hasSchema) refs.push({ sectionId: "audit-schema-forecast", label: "Schema Opportunity Forecast" });
  if (page.internalLinks.length === 0) refs.push({ sectionId: "audit-key-findings", label: "Key Findings" });
  refs.push({ sectionId: "audit-priority-actions", label: "Priority Actions" });
  return refs.slice(0, 3);
}

// ─── Page selection ───────────────────────────────────────────────────────────

function selectPages(
  pages: PageData[],
  aiAttribution: AiAttributionContext | null,
): PageData[] {
  const selected: PageData[] = [];
  const seen = new Set<string>();

  function add(page: PageData) {
    const key = normUrlSimple(page.url);
    if (!seen.has(key)) {
      seen.add(key);
      selected.push(page);
    }
  }

  // 1. Homepage (always)
  const homepage = pages.find((p) => isHomepage(p.url));
  if (homepage) add(homepage);

  // 2. Contact page (always if present)
  const contact = pages.find((p) => isContactPage(p.url));
  if (contact) add(contact);

  // 3. Top 3-5 service/product pages by inbound internal link count
  const inboundCounts = new Map<string, number>();
  for (const page of pages) {
    for (const link of page.internalLinks) {
      const key = normUrlSimple(link);
      inboundCounts.set(key, (inboundCounts.get(key) ?? 0) + 1);
    }
  }

  const servicePages = pages
    .filter((p) => {
      const type = classifyPageType(p.url);
      return type === "service" || type === "product" || type === "pricing";
    })
    .sort((a, b) => (inboundCounts.get(normUrlSimple(b.url)) ?? 0) - (inboundCounts.get(normUrlSimple(a.url)) ?? 0))
    .slice(0, 5);
  for (const p of servicePages) add(p);

  // 4. 1st-party: top AI-visited + converting pages (add up to 3)
  if (aiAttribution && selected.length < 10) {
    const topAiPages = aiAttribution.topPages
      .filter((ap) => ap.conversions > 0 || ap.aiVisits >= 3)
      .sort((a, b) => b.conversions * 3 + b.aiVisits - (a.conversions * 3 + a.aiVisits))
      .slice(0, 3);
    for (const ap of topAiPages) {
      const match = pages.find((p) => normUrlSimple(p.url) === normUrlSimple(ap.url));
      if (match) add(match);
    }
  }

  // 5. Fill up to 10 with highest-inbound pages not already selected
  if (selected.length < 10) {
    const remaining = pages
      .filter((p) => !seen.has(normUrlSimple(p.url)) && p.isIndexable)
      .sort((a, b) => (inboundCounts.get(normUrlSimple(b.url)) ?? 0) - (inboundCounts.get(normUrlSimple(a.url)) ?? 0));
    for (const p of remaining) {
      if (selected.length >= 10) break;
      add(p);
    }
  }

  return selected.slice(0, 10);
}

function buildSelectionReason(
  page: PageData,
  aiAttribution: AiAttributionContext | null,
  inboundCounts: Map<string, number>,
): string {
  if (isHomepage(page.url)) return "Homepage — primary brand entry point";
  if (isContactPage(page.url)) return "Contact page — primary conversion destination";

  const aiPage = aiAttribution?.topPages.find((ap) => normUrlSimple(ap.url) === normUrlSimple(page.url));
  if (aiPage && aiPage.conversions > 0) return `AI-attributed page — ${aiPage.aiVisits} AI visits and ${aiPage.conversions} conversion${aiPage.conversions !== 1 ? "s" : ""} in 30 days`;
  if (aiPage) return `AI-visited page — ${aiPage.aiVisits} AI visits in trailing 30 days`;

  const inbound = inboundCounts.get(normUrlSimple(page.url)) ?? 0;
  const type = classifyPageType(page.url);
  if (type === "service" || type === "product") return `Service page — ${inbound} inbound internal link${inbound !== 1 ? "s" : ""}`;
  if (inbound > 0) return `High-priority page — ${inbound} inbound internal link${inbound !== 1 ? "s" : ""}`;
  return "Structurally important page included in review";
}

// ─── Main export ──────────────────────────────────────────────────────────────

export function aggregatePageStrategicPass(
  crawlData: PageData[],
  firecrawlPages: ContentSample[],
  behavioralData: BehavioralContext | null,
  aiAttribution: AiAttributionContext | null,
): PageStrategicPass | null {
  if (crawlData.length === 0) return null;

  // Build inbound link counts (used for selection + selection reason)
  const inboundCounts = new Map<string, number>();
  for (const page of crawlData) {
    for (const link of page.internalLinks) {
      const key = normUrlSimple(link);
      inboundCounts.set(key, (inboundCounts.get(key) ?? 0) + 1);
    }
  }

  const sampleMap = new Map<string, ContentSample>();
  for (const s of firecrawlPages) {
    sampleMap.set(normUrlSimple(s.url), s);
  }

  const selectedPages = selectPages(crawlData, aiAttribution);
  if (selectedPages.length === 0) return null;

  const cards: PageHealthCard[] = selectedPages.map((page) => {
    const sample = sampleMap.get(normUrlSimple(page.url));
    const score = computePageHealthScore(page, sample, behavioralData);
    const allIssues = detectPageIssues(page, sample, behavioralData);
    const topIssues = allIssues.slice(0, 3).map((i) => ({ severity: i.severity, issue: i.issue, fix: i.fix }));
    const topRecommendation = buildTopRecommendation(allIssues, page, sample);
    const crossReferences = buildCrossReferences(page);
    const selectionReason = buildSelectionReason(page, aiAttribution, inboundCounts);

    const behavPage = behavioralData?.pages.find((p) => normUrlSimple(p.url) === normUrlSimple(page.url));
    const aiPage = aiAttribution?.topPages.find((ap) => normUrlSimple(ap.url) === normUrlSimple(page.url));

    return {
      url: page.url,
      pageType: classifyPageType(page.url),
      healthScore: score,
      selectionReason,
      structuralSignals: {
        hasH1: !!page.h1,
        h1Text: page.h1 ?? null,
        hasSchema: page.hasSchema,
        schemaTypes: page.schemaTypes,
        wordCount: page.wordCount ?? sample?.wordCount ?? null,
        hasCTA: sample?.hasCTA ?? null,
        ctaExamples: sample?.ctaExamples ?? [],
        hasPhoneClick: sample?.hasPhoneClick ?? null,
        hasFAQ: sample?.hasFAQ ?? null,
        internalLinkCount: page.internalLinks.length,
        isIndexable: page.isIndexable,
        metaDescription: page.metaDescription ?? null,
      },
      behavioralSignals: behavPage
        ? {
            bounceRate: behavPage.bounceRate,
            avgSessionDuration: behavPage.avgSessionDuration,
            conversionCount: behavPage.conversionCount,
            conversionValue: behavPage.conversionValue,
          }
        : null,
      aiSignals: aiPage
        ? {
            aiVisits30d: aiPage.aiVisits,
            conversions30d: aiPage.conversions,
          }
        : null,
      topIssues,
      topRecommendation,
      crossReferences,
    };
  });

  return { pages: cards };
}
