/**
 * Behavioral Action Bridge — Signal Center + Priority Action Cross-Reference
 * ─────────────────────────────────────────────────────────────────────────────
 * Post-synthesis enrichment: matches synthesizer priority actions to behavioral
 * signals from Signal Center to produce quantified impact estimates.
 * Also surfaces new findings that only emerge from behavioral cross-referencing.
 *
 * MUST run after synthesizeSeoSection() completes — depends on priorityActions output.
 * 1st-party only — returns null when Signal Center confidence is low or absent.
 *
 * Internal module — no third-party vendor names in user-facing output.
 */

import type {
  SeoAction,
  PrioritizedFinding,
  SignalCenter,
  PageStrategicPass,
  AiAttributionContext,
  BehavioralActionBridge,
} from "../types";

// ─── Helpers ──────────────────────────────────────────────────────────────────

function roundTo(n: number, decimals = 1): number {
  return Math.round(n * Math.pow(10, decimals)) / Math.pow(10, decimals);
}

function titleMatchesKeyword(title: string, keywords: string[]): boolean {
  const lower = title.toLowerCase();
  return keywords.some((kw) => lower.includes(kw));
}

// ─── Action → behavioral signal matching ─────────────────────────────────────

function buildEnhancedAction(
  action: SeoAction,
  signalCenter: SignalCenter,
  aiAttribution: AiAttributionContext | null,
): BehavioralActionBridge["enhancedActions"][number] | null {
  const evidence: BehavioralActionBridge["enhancedActions"][number]["behavioralEvidence"] = [];
  let quantifiedImpact: BehavioralActionBridge["enhancedActions"][number]["quantifiedImpactEstimate"] | null = null;
  let rewritten = action.rationale;

  const title = action.title.toLowerCase();

  // ── Form / CTA / booking actions ─────────────────────────────────────────
  const isFormAction = titleMatchesKeyword(action.title, ["form", "cta", "call-to-action", "booking", "contact", "lead", "conversion", "convert"]);
  if (isFormAction && signalCenter.formAbandonment && signalCenter.formAbandonment.abandonRate > 0) {
    const fa = signalCenter.formAbandonment;
    const topField = fa.topAbandonedFields[0];
    const abandonPct = roundTo(fa.abandonRate * 100);
    const completionRate = roundTo((1 - fa.abandonRate) * 100);
    const projectedCompletionRate = Math.min(99, completionRate + abandonPct * 0.3);

    evidence.push({
      signal: `Form abandonment at ${abandonPct}% — ${fa.totalStarts.toLocaleString()} starts, ${fa.totalCompletes.toLocaleString()} completions`,
      source: "signal-center",
      dataPoints: {
        abandonRate: fa.abandonRate,
        totalStarts: fa.totalStarts,
        totalCompletes: fa.totalCompletes,
        topFieldAbandonRate: topField?.abandonRate ?? 0,
      },
    });

    if (topField) {
      evidence.push({
        signal: `"${topField.fieldName}" field causes ${roundTo(topField.abandonRate * 100)}% abandonment — ${topField.medianTimeSpent}s avg time spent before abandoning`,
        source: "signal-center",
        dataPoints: {
          fieldAbandonRate: topField.abandonRate,
          medianTimeSpent: topField.medianTimeSpent,
          errorRate: topField.errorRate,
        },
      });
    }

    const liftMin = Math.round(abandonPct * 0.15);
    const liftMax = Math.round(abandonPct * 0.30);
    quantifiedImpact = {
      metric: "form completion rate",
      currentValue: completionRate,
      projectedValue: roundTo(projectedCompletionRate),
      liftRange: { min: liftMin, max: liftMax },
      confidence: fa.totalStarts >= 50 ? "moderate" : "low",
    };

    rewritten = topField
      ? `Behavioral telemetry shows ${abandonPct}% of form visitors abandon before completing — the "${topField.fieldName}" field causes ${roundTo(topField.abandonRate * 100)}% of those drop-offs with ${topField.medianTimeSpent}s median time before abandonment. ${action.rationale} Removing or making this field optional is projected to lift form completion by ${liftMin}-${liftMax} percentage points.`
      : `Behavioral telemetry shows ${abandonPct}% form abandonment (${fa.totalStarts.toLocaleString()} starts, only ${fa.totalCompletes.toLocaleString()} completions). ${action.rationale}`;
  }

  // ── Schema / structured data actions ─────────────────────────────────────
  const isSchemaAction = titleMatchesKeyword(action.title, ["schema", "structured data", "json-ld", "markup"]);
  if (isSchemaAction && aiAttribution) {
    const aiConvRate = aiAttribution.totalAiReferrals > 0
      ? roundTo((aiAttribution.totalConversions30d / aiAttribution.totalAiReferrals) * 100)
      : 0;
    evidence.push({
      signal: `AI traffic: ${aiAttribution.totalAiReferrals} referrals with ${aiConvRate}% conversion rate — schema gaps limit AI Overview citation eligibility`,
      source: "ai-attribution",
      dataPoints: {
        totalAiReferrals: aiAttribution.totalAiReferrals,
        totalConversions30d: aiAttribution.totalConversions30d,
        aiConversionRate: aiConvRate,
      },
    });

    if (!quantifiedImpact && aiAttribution.totalAiReferrals > 0) {
      quantifiedImpact = {
        metric: "AI referral sessions (30d)",
        currentValue: aiAttribution.totalAiReferrals,
        projectedValue: Math.round(aiAttribution.totalAiReferrals * 1.2),
        liftRange: { min: 10, max: 30 },
        confidence: "low",
      };
      rewritten = `First-party data shows ${aiAttribution.totalAiReferrals} AI referrals generating ${aiAttribution.totalConversions30d} conversions at a ${aiConvRate}% rate. ${action.rationale} Schema deployment is projected to expand AI Overview citation eligibility by 10-30%, directly amplifying this revenue stream.`;
    }
  }

  // ── Mobile / device performance actions ──────────────────────────────────
  const isMobileAction = titleMatchesKeyword(action.title, ["mobile", "responsive", "page speed", "cwv", "core web", "performance", "lcp", "cls"]);
  if (isMobileAction && signalCenter.deviceGeo) {
    const dg = signalCenter.deviceGeo;
    const mobileDelta = dg.mobileDesktopDelta;
    const mobileConvPct = roundTo(dg.deviceMix.mobile.conversionRate * 100);
    const desktopConvPct = roundTo(dg.deviceMix.desktop.conversionRate * 100);

    evidence.push({
      signal: `Mobile conversion rate: ${mobileConvPct}% vs desktop: ${desktopConvPct}% — ${roundTo(Math.abs(mobileDelta) * 100)}% gap in favor of desktop`,
      source: "signal-center",
      dataPoints: {
        mobileConvRate: dg.deviceMix.mobile.conversionRate,
        desktopConvRate: dg.deviceMix.desktop.conversionRate,
        mobileSessions: dg.deviceMix.mobile.sessions,
        desktopSessions: dg.deviceMix.desktop.sessions,
        mobileDelta,
      },
    });

    if (!quantifiedImpact && mobileDelta < -0.01) {
      const projectedMobileConv = mobileConvPct + roundTo(Math.abs(mobileDelta) * 100 * 0.4);
      quantifiedImpact = {
        metric: "mobile conversion rate",
        currentValue: mobileConvPct,
        projectedValue: roundTo(projectedMobileConv),
        liftRange: { min: Math.round(Math.abs(mobileDelta) * 100 * 15), max: Math.round(Math.abs(mobileDelta) * 100 * 40) },
        confidence: dg.deviceMix.mobile.sessions >= 100 ? "moderate" : "low",
      };
      rewritten = `Session telemetry shows mobile converts at ${mobileConvPct}% vs desktop at ${desktopConvPct}% — a ${roundTo(Math.abs(mobileDelta) * 100)}% gap. Mobile accounts for ${roundTo(dg.deviceMix.mobile.share * 100)}% of sessions but underperforms significantly. ${action.rationale}`;
    }
  }

  // ── Scroll / content depth actions ───────────────────────────────────────
  const isContentAction = titleMatchesKeyword(action.title, ["content", "h1", "heading", "copy", "text", "rewrite", "meta"]);
  if (isContentAction && signalCenter.scrollToConversion) {
    const scroll = signalCenter.scrollToConversion;
    const medianDepth = roundTo(scroll.medianScrollDepthBeforeConversion * 100);
    if (scroll.highScrollLowConvPages.length > 0) {
      const problematic = scroll.highScrollLowConvPages[0]!;
      const pagePath = problematic.url.replace(/^https?:\/\/[^/]+/, "") || "/";
      evidence.push({
        signal: `${pagePath} has ${roundTo(problematic.avgScroll * 100)}% avg scroll depth but only ${problematic.conversions} conversions — visitors read but don't convert`,
        source: "signal-center",
        dataPoints: {
          avgScroll: problematic.avgScroll,
          conversions: problematic.conversions,
          medianScrollDepthBeforeConversion: scroll.medianScrollDepthBeforeConversion,
        },
      });

      if (!quantifiedImpact) {
        quantifiedImpact = {
          metric: "page conversion count",
          currentValue: problematic.conversions,
          projectedValue: Math.round(problematic.conversions * 1.3),
          liftRange: { min: 15, max: 40 },
          confidence: "low",
        };
        rewritten = `Behavioral telemetry shows ${pagePath} achieves ${roundTo(problematic.avgScroll * 100)}% average scroll depth — visitors engage deeply with the content — but only ${problematic.conversions} conversions resulted. A CTA placed at the ${Math.round(scroll.medianScrollDepthBeforeConversion * 100)}% scroll point (where conversions historically occur) would capture more of this engaged audience. ${action.rationale}`;
      }
    }
  }

  // ── Friction / rage click actions ─────────────────────────────────────────
  const isTechnicalAction = titleMatchesKeyword(action.title, ["fix", "broken", "redirect", "error", "404", "link", "button", "click"]);
  if (isTechnicalAction && signalCenter.topFrictionEvents.length > 0) {
    const critical = signalCenter.topFrictionEvents.filter((e) => e.severity === "critical");
    if (critical.length > 0) {
      const top = critical[0]!;
      evidence.push({
        signal: `${top.count.toLocaleString()} ${top.displayName} detected — severity: ${top.severity}`,
        source: "signal-center",
        dataPoints: {
          count: top.count,
          affectedPages: top.topAffectedPages.length,
        },
      });

      if (!rewritten.includes("friction") && !rewritten.includes("rage") && !rewritten.includes("click")) {
        rewritten = `Friction telemetry shows ${top.count.toLocaleString()} ${top.displayName} events — critical UX signals indicating broken or unresponsive elements. ${action.rationale}`;
      }
    }
  }

  // Only return enhanced version if we found behavioral evidence
  if (evidence.length === 0 || !quantifiedImpact) return null;

  return {
    originalActionTitle: action.title,
    behavioralEvidence: evidence,
    quantifiedImpactEstimate: quantifiedImpact,
    rewrittenActionDescription: rewritten,
  };
}

// ─── New behavioral findings ──────────────────────────────────────────────────

function deriveNewBehavioralFindings(
  signalCenter: SignalCenter,
  existingActionTitles: string[],
  aiAttribution: AiAttributionContext | null,
): BehavioralActionBridge["newBehavioralFindings"] {
  const findings: BehavioralActionBridge["newBehavioralFindings"] = [];
  const existingLower = existingActionTitles.map((t) => t.toLowerCase());

  // Mobile AI traffic bounce pattern
  if (signalCenter.deviceGeo && aiAttribution && aiAttribution.totalAiReferrals > 0) {
    const dg = signalCenter.deviceGeo;
    const mobileDelta = dg.mobileDesktopDelta;
    const hasExistingMobileAction = existingLower.some((t) => t.includes("mobile") || t.includes("responsive"));
    if (mobileDelta < -0.02 && !hasExistingMobileAction) {
      findings.push({
        title: "Mobile AI Traffic Conversion Gap Not Addressed by Current Actions",
        severity: "high",
        description: `Mobile converts at ${roundTo(dg.deviceMix.mobile.conversionRate * 100)}% vs desktop at ${roundTo(dg.deviceMix.desktop.conversionRate * 100)}% — a ${roundTo(Math.abs(mobileDelta) * 100)}% gap. Given that AI referral traffic skews mobile on most queries, this gap likely depresses AI-attributed conversion rates below their true potential.`,
        behavioralEvidence: [
          `Mobile sessions: ${dg.deviceMix.mobile.sessions.toLocaleString()} at ${roundTo(dg.deviceMix.mobile.conversionRate * 100)}% conversion rate`,
          `Desktop sessions: ${dg.deviceMix.desktop.sessions.toLocaleString()} at ${roundTo(dg.deviceMix.desktop.conversionRate * 100)}% conversion rate`,
          `Mobile share of total sessions: ${roundTo(dg.deviceMix.mobile.share * 100)}%`,
        ],
        affectedUrls: [],
        recommendation: `Implement mobile-specific CTA treatment on the top AI-cited landing pages. Reduce form field count for mobile visitors. Ensure above-fold content is visible without scrolling on 375px viewports.`,
      });
    }
  }

  // High scroll engagement pages with CTA gap
  if (signalCenter.scrollToConversion?.highScrollLowConvPages.length >= 2) {
    const pages = signalCenter.scrollToConversion.highScrollLowConvPages.slice(0, 3);
    const hasExistingScrollAction = existingLower.some((t) => t.includes("cta") || t.includes("scroll") || t.includes("conversion path"));
    if (!hasExistingScrollAction) {
      findings.push({
        title: "High-Engagement Pages Without Conversion Pathway Optimization",
        severity: "high",
        description: `${pages.length} pages show above-average scroll depth (visitors read deeply) but generate minimal conversions. This is a CTA positioning problem, not a content problem — visitors are engaged but don't reach the conversion mechanism.`,
        behavioralEvidence: pages.map((p) => `${p.url.replace(/^https?:\/\/[^/]+/, "") || "/"}: ${roundTo(p.avgScroll * 100)}% avg scroll, ${p.conversions} conversions`),
        affectedUrls: pages.map((p) => p.url),
        recommendation: `Insert a sticky CTA bar at the ${Math.round(signalCenter.scrollToConversion.medianScrollDepthBeforeConversion * 100)}% scroll depth point on these pages. Use a phone number + brief action text. Test removing footer-only CTA placement.`,
      });
    }
  }

  // Form abandonment with specific high-impact fields not in existing actions
  if (signalCenter.formAbandonment && signalCenter.formAbandonment.abandonRate > 0.4) {
    const fa = signalCenter.formAbandonment;
    const highRiskFields = fa.topAbandonedFields.filter((f) => f.abandonRate > 0.25);
    const hasExistingFormAction = existingLower.some((t) => t.includes("form") || t.includes("field") || t.includes("abandonment"));
    if (highRiskFields.length > 0 && !hasExistingFormAction) {
      findings.push({
        title: "Form Field Friction Causing Measurable Conversion Loss",
        severity: "critical",
        description: `Overall form abandonment is ${roundTo(fa.abandonRate * 100)}% (${fa.totalStarts.toLocaleString()} starts vs ${fa.totalCompletes.toLocaleString()} completions). Specific fields drive disproportionate abandonment: ${highRiskFields.slice(0, 2).map((f) => `"${f.fieldName}" at ${roundTo(f.abandonRate * 100)}%`).join(", ")}.`,
        behavioralEvidence: [
          `Total form starts: ${fa.totalStarts.toLocaleString()}`,
          `Total completions: ${fa.totalCompletes.toLocaleString()}`,
          ...highRiskFields.slice(0, 3).map((f) => `"${f.fieldName}": ${roundTo(f.abandonRate * 100)}% abandon rate, ${f.medianTimeSpent}s spent, ${roundTo(f.errorRate * 100)}% error rate`),
        ],
        affectedUrls: [],
        recommendation: highRiskFields[0]!.recommendation,
      });
    }
  }

  // Channel conversion rate gap
  const channelMix = signalCenter.channelMix;
  const phone = channelMix.phone;
  const form = channelMix.form;
  if (phone.conversionRate !== null && form.conversionRate !== null && form.conversionRate > 0) {
    const phoneRate = phone.conversionRate;
    const formRate = form.conversionRate;
    const hasExistingPhoneAction = existingLower.some((t) => t.includes("phone") || t.includes("call") || t.includes("click-to-call"));
    if (phoneRate > formRate * 1.5 && phone.share < 0.4 && !hasExistingPhoneAction) {
      findings.push({
        title: "Phone Channel Converts Significantly Better Than Form but Is Underutilized",
        severity: "moderate",
        description: `Phone channel converts at ${roundTo(phoneRate * 100)}% vs form at ${roundTo(formRate * 100)}% — a ${roundTo((phoneRate / formRate - 1) * 100)}% conversion advantage. But phone accounts for only ${roundTo(phone.share * 100)}% of conversion events. Prominently displaying the phone number on high-traffic pages would shift volume toward the higher-converting channel.`,
        behavioralEvidence: [
          `Phone conversions: ${phone.count} (${roundTo(phone.share * 100)}% share)`,
          `Form conversions: ${form.count} (${roundTo(form.share * 100)}% share)`,
          `Phone conversion rate: ${roundTo(phoneRate * 100)}%`,
          `Form conversion rate: ${roundTo(formRate * 100)}%`,
        ],
        affectedUrls: [],
        recommendation: `Add click-to-call button above the fold on the homepage and top service pages. Ensure phone number is visible in the navigation header on mobile. Test replacing "Contact Us" form links with phone number + callback option.`,
      });
    }
  }

  return findings.slice(0, 4);
}

// ─── Top behavioral patterns ──────────────────────────────────────────────────

function deriveTopBehavioralPatterns(
  signalCenter: SignalCenter,
  enhancedActions: BehavioralActionBridge["enhancedActions"],
): BehavioralActionBridge["topBehavioralPatterns"] {
  const patterns: BehavioralActionBridge["topBehavioralPatterns"] = [];

  // Pattern 1: Conversion funnel dropoff
  const funnelDropoff = signalCenter.conversionFunnel.biggestDropoff;
  patterns.push({
    pattern: `${funnelDropoff.percent}% session dropoff between ${funnelDropoff.from} and ${funnelDropoff.to} stages`,
    impact: `The majority of visitors who reach the ${funnelDropoff.from} stage don't progress to ${funnelDropoff.to} — this is the highest-leverage conversion choke point in the funnel`,
    relatedActions: enhancedActions.filter((a) =>
      a.originalActionTitle.toLowerCase().includes("cta") ||
      a.originalActionTitle.toLowerCase().includes("form") ||
      a.originalActionTitle.toLowerCase().includes("contact")
    ).map((a) => a.originalActionTitle).slice(0, 2),
  });

  // Pattern 2: Friction events
  const criticalFriction = signalCenter.topFrictionEvents.filter((e) => e.severity === "critical");
  if (criticalFriction.length > 0) {
    const topFriction = criticalFriction[0]!;
    patterns.push({
      pattern: `${topFriction.count.toLocaleString()} ${topFriction.displayName} across ${topFriction.topAffectedPages.length} pages`,
      impact: `Critical UX friction signals indicate broken or unresponsive UI elements that actively interrupt conversion intent`,
      relatedActions: enhancedActions.filter((a) =>
        a.originalActionTitle.toLowerCase().includes("fix") ||
        a.originalActionTitle.toLowerCase().includes("button") ||
        a.originalActionTitle.toLowerCase().includes("technical")
      ).map((a) => a.originalActionTitle).slice(0, 2),
    });
  }

  // Pattern 3: Device mix conversion gap
  if (signalCenter.deviceGeo && Math.abs(signalCenter.deviceGeo.mobileDesktopDelta) > 0.01) {
    const dg = signalCenter.deviceGeo;
    patterns.push({
      pattern: `${roundTo(Math.abs(dg.mobileDesktopDelta) * 100)}% conversion gap between mobile and desktop`,
      impact: `Mobile accounts for ${roundTo(dg.deviceMix.mobile.share * 100)}% of sessions but converts at a significantly different rate — cross-device parity is a revenue multiplier`,
      relatedActions: enhancedActions.filter((a) =>
        a.originalActionTitle.toLowerCase().includes("mobile") ||
        a.originalActionTitle.toLowerCase().includes("performance")
      ).map((a) => a.originalActionTitle).slice(0, 2),
    });
  }

  return patterns.slice(0, 5);
}

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

export function aggregateBehavioralActionBridge(
  priorityActions: SeoAction[],
  prioritizedFindings: PrioritizedFinding[] | undefined,
  signalCenter: SignalCenter,
  pageStrategicPass: PageStrategicPass | null,
  aiAttribution: AiAttributionContext | null,
): BehavioralActionBridge | null {
  if (!signalCenter || signalCenter.dataConfidence === "low") return null;

  // Match actions to behavioral signals
  const enhancedActions: BehavioralActionBridge["enhancedActions"] = [];
  for (const action of priorityActions) {
    try {
      const enhanced = buildEnhancedAction(action, signalCenter, aiAttribution);
      if (enhanced) enhancedActions.push(enhanced);
    } catch {
      // Skip action on error — don't break the bridge
    }
  }

  const newBehavioralFindings = deriveNewBehavioralFindings(
    signalCenter,
    priorityActions.map((a) => a.title),
    aiAttribution,
  );

  const topBehavioralPatterns = deriveTopBehavioralPatterns(signalCenter, enhancedActions);

  // Return null only if there's genuinely nothing to show
  if (enhancedActions.length === 0 && newBehavioralFindings.length === 0 && topBehavioralPatterns.length === 0) {
    return null;
  }

  return {
    hasBehavioralOverlay: true,
    enhancedActions,
    newBehavioralFindings,
    topBehavioralPatterns,
  };
}
