/**
 * AI Snippet Eligibility Analysis
 * ─────────────────────────────────
 * For each tracked industry query, identifies which SERP features are present
 * and assesses the brand's eligibility for each. Returns null when no SERP data
 * is available.
 *
 * Internal module — vendor names must not appear in user-facing output.
 */

import type { PageData } from "../types";
import type { AiSnippetEligibility, SnippetFeatureEligibility } from "../types";
import type { ContentSample } from "../scrapers/firecrawl-content";
import type { SerpAnalysis } from "./serp-analysis";

export type { AiSnippetEligibility };

type SerpFeature =
  | "featured_snippet"
  | "faq"
  | "knowledge_panel"
  | "image_pack"
  | "video_carousel"
  | "people_also_ask"
  | "local_pack"
  | "ai_overview"
  | "product_carousel"
  | "reviews_carousel";

// ─── Eligibility helpers ──────────────────────────────────────────────────────

function hasFaqSchema(pages: PageData[]): boolean {
  return pages.some((p) => p.schemaTypes.includes("FAQPage"));
}

function hasFaqContent(samples: ContentSample[]): boolean {
  return samples.some((s) => /faq|frequently asked|q:\s|a:\s/i.test(s.markdown) || s.hasFAQ);
}

function hasOrganizationSchema(pages: PageData[]): boolean {
  return pages.some((p) =>
    p.schemaTypes.some((t) =>
      ["Organization", "LocalBusiness", "MedicalOrganization", "LegalService"].includes(t)
    )
  );
}

function hasImageObjectSchema(pages: PageData[]): boolean {
  return pages.some((p) => p.schemaTypes.includes("ImageObject"));
}

function hasVideoObjectSchema(pages: PageData[]): boolean {
  return pages.some((p) => p.schemaTypes.includes("VideoObject"));
}

function hasQuestionHeaders(samples: ContentSample[]): boolean {
  return samples.some((s) => /^##?\s+.*\?/m.test(s.markdown));
}

function hasLocalBusinessSchema(pages: PageData[]): boolean {
  return pages.some((p) => p.schemaTypes.includes("LocalBusiness"));
}

function hasAggregateRatingSchema(pages: PageData[]): boolean {
  return pages.some((p) => p.schemaTypes.includes("AggregateRating") || p.schemaTypes.includes("LocalBusiness"));
}

function hasProductSchema(pages: PageData[]): boolean {
  return pages.some((p) => p.schemaTypes.includes("Product"));
}

function hasDefinitionContent(samples: ContentSample[]): boolean {
  // Look for a short block of 40-60 word answers (definition-style)
  return samples.some((s) => {
    const paras = s.markdown.split(/\n+/);
    return paras.some((p) => {
      const words = p.trim().split(/\s+/).length;
      return words >= 30 && words <= 80;
    });
  });
}

function countSchemaTypes(pages: PageData[]): number {
  return new Set(pages.flatMap((p) => p.schemaTypes)).size;
}

// ─── Per-feature eligibility assessment ──────────────────────────────────────

function assessFeatureEligibility(
  feature: SerpFeature,
  pages: PageData[],
  samples: ContentSample[],
  brandRanking: number | null,
): SnippetFeatureEligibility {
  switch (feature) {
    case "featured_snippet": {
      const hasDefinition = hasDefinitionContent(samples);
      const eligible = hasDefinition && brandRanking != null && brandRanking <= 10;
      const partial = hasDefinition || (brandRanking != null && brandRanking <= 10);
      const missingFactors: string[] = [];
      if (!hasDefinition) missingFactors.push("Add 40-60 word definition-style answer blocks at the top of key pages");
      if (!brandRanking || brandRanking > 10) missingFactors.push("Brand must rank in top 10 for featured snippet eligibility");
      return {
        feature: "Featured Snippet",
        currentlyEligible: eligible,
        eligibilityStatus: eligible ? "eligible" : partial ? "partial" : "not_eligible",
        missingFactors,
        implementationDifficulty: "easy",
        estimatedTraffic: "high",
      };
    }

    case "faq": {
      const schemaPresent = hasFaqSchema(pages);
      const contentPresent = hasFaqContent(samples);
      const eligible = schemaPresent && contentPresent;
      const partial = schemaPresent || contentPresent;
      const missingFactors: string[] = [];
      if (!schemaPresent) missingFactors.push("Add FAQPage JSON-LD schema to pages with Q&A content");
      if (!contentPresent) missingFactors.push("Add visible question-and-answer content sections");
      return {
        feature: "FAQ Rich Result",
        currentlyEligible: eligible,
        eligibilityStatus: eligible ? "eligible" : partial ? "partial" : "not_eligible",
        missingFactors,
        implementationDifficulty: "easy",
        estimatedTraffic: "moderate",
      };
    }

    case "knowledge_panel": {
      const hasOrgSchema = hasOrganizationSchema(pages);
      const sameAsPresent = pages.some((p) =>
        JSON.stringify(p).includes('"sameAs"')
      );
      const eligible = hasOrgSchema && sameAsPresent;
      const partial = hasOrgSchema;
      const missingFactors: string[] = [];
      if (!hasOrgSchema) missingFactors.push("Add Organization schema with complete entity data");
      if (!sameAsPresent) missingFactors.push("Add sameAs links to Google Business Profile, LinkedIn, and Wikipedia/Wikidata");
      return {
        feature: "Knowledge Panel",
        currentlyEligible: eligible,
        eligibilityStatus: eligible ? "eligible" : partial ? "partial" : "not_eligible",
        missingFactors,
        implementationDifficulty: "moderate",
        estimatedTraffic: "high",
      };
    }

    case "image_pack": {
      const hasSchema = hasImageObjectSchema(pages);
      const hasAltText = pages.some((p) => p.images?.some((img) => img.alt && img.alt.length > 5));
      const eligible = hasSchema && hasAltText;
      const partial = hasAltText;
      const missingFactors: string[] = [];
      if (!hasSchema) missingFactors.push("Add ImageObject schema with name, description, and contentUrl");
      if (!hasAltText) missingFactors.push("Add descriptive alt text to all images");
      return {
        feature: "Image Pack",
        currentlyEligible: eligible,
        eligibilityStatus: eligible ? "eligible" : partial ? "partial" : "not_eligible",
        missingFactors,
        implementationDifficulty: "easy",
        estimatedTraffic: "moderate",
      };
    }

    case "video_carousel": {
      const hasSchema = hasVideoObjectSchema(pages);
      const missingFactors: string[] = [];
      if (!hasSchema) missingFactors.push("Add VideoObject schema and embed video content on key pages");
      return {
        feature: "Video Carousel",
        currentlyEligible: hasSchema,
        eligibilityStatus: hasSchema ? "eligible" : "not_eligible",
        missingFactors,
        implementationDifficulty: "hard",
        estimatedTraffic: "moderate",
      };
    }

    case "people_also_ask": {
      const hasQHeaders = hasQuestionHeaders(samples);
      const hasFaq = hasFaqContent(samples);
      const eligible = hasQHeaders || hasFaq;
      const missingFactors: string[] = [];
      if (!hasQHeaders) missingFactors.push("Add question-format H2 headings to service and content pages");
      if (!hasFaq) missingFactors.push("Add FAQ sections with common customer questions");
      return {
        feature: "People Also Ask",
        currentlyEligible: eligible,
        eligibilityStatus: eligible ? "eligible" : "not_eligible",
        missingFactors,
        implementationDifficulty: "easy",
        estimatedTraffic: "moderate",
      };
    }

    case "local_pack": {
      const hasSchema = hasLocalBusinessSchema(pages);
      const missingFactors: string[] = [];
      if (!hasSchema) missingFactors.push("Add LocalBusiness schema with full NAP, hours, and geo-coordinates");
      return {
        feature: "Local Pack",
        currentlyEligible: hasSchema,
        eligibilityStatus: hasSchema ? "eligible" : "not_eligible",
        missingFactors,
        implementationDifficulty: "easy",
        estimatedTraffic: "high",
      };
    }

    case "ai_overview": {
      const schemaCount = countSchemaTypes(pages);
      const hasAuth = pages.some((p) => (p.externalLinks?.length ?? 0) > 2);
      const eligible = schemaCount >= 2 && hasDefinitionContent(samples);
      const partial = schemaCount >= 1 || hasDefinitionContent(samples);
      const missingFactors: string[] = [];
      if (schemaCount < 2) missingFactors.push("Deploy multiple schema types — AI Overview eligibility requires comprehensive structured data");
      if (!hasDefinitionContent(samples)) missingFactors.push("Add comprehensive definition-style content for key topics");
      if (!hasAuth) missingFactors.push("Strengthen E-E-A-T signals and authority backlinks");
      return {
        feature: "AI Overview",
        currentlyEligible: eligible,
        eligibilityStatus: eligible ? "eligible" : partial ? "partial" : "not_eligible",
        missingFactors,
        implementationDifficulty: "hard",
        estimatedTraffic: "high",
      };
    }

    case "product_carousel": {
      const hasSchema = hasProductSchema(pages);
      const hasRating = hasAggregateRatingSchema(pages);
      const eligible = hasSchema && hasRating;
      const partial = hasSchema;
      const missingFactors: string[] = [];
      if (!hasSchema) missingFactors.push("Add Product schema with name, price, and availability");
      if (!hasRating) missingFactors.push("Add AggregateRating to Product schema");
      return {
        feature: "Product Carousel",
        currentlyEligible: eligible,
        eligibilityStatus: eligible ? "eligible" : partial ? "partial" : "not_eligible",
        missingFactors,
        implementationDifficulty: "moderate",
        estimatedTraffic: "high",
      };
    }

    case "reviews_carousel": {
      const hasSchema = hasAggregateRatingSchema(pages);
      const missingFactors: string[] = [];
      if (!hasSchema) missingFactors.push("Add AggregateRating schema with reviewCount and ratingValue");
      return {
        feature: "Reviews Carousel",
        currentlyEligible: hasSchema,
        eligibilityStatus: hasSchema ? "eligible" : "not_eligible",
        missingFactors,
        implementationDifficulty: "easy",
        estimatedTraffic: "moderate",
      };
    }
  }
}

// ─── SERP feature inference from available data ───────────────────────────────

function inferSerpFeaturesForQuery(
  keyword: string,
  serpAnalysis: SerpAnalysis,
  isLocal: boolean,
): SerpFeature[] {
  const features: SerpFeature[] = [];

  // AI Overview — check from serpAnalysis
  const aoEntry = serpAnalysis.aiOverviewPresence.find((a) =>
    a.keyword.toLowerCase() === keyword.toLowerCase()
  );
  if (aoEntry?.hasAiOverview) features.push("ai_overview");

  // Infer likely features from query intent
  const kw = keyword.toLowerCase();
  if (/what\s+is|how\s+to|definition|meaning|explain/i.test(kw)) {
    features.push("featured_snippet", "people_also_ask");
  }
  if (/faq|question|\?$/.test(kw)) {
    features.push("faq", "people_also_ask");
  }
  if (/near\s+me|in\s+[a-z]+|local|best\s+[a-z]+\s+in/i.test(kw) || isLocal) {
    features.push("local_pack");
  }
  if (/review|rating|best|top\s+\d+|compare/i.test(kw)) {
    features.push("reviews_carousel");
  }
  if (/buy|price|cost|cheap|product/i.test(kw)) {
    features.push("product_carousel");
  }
  if (/image|photo|picture|gallery/i.test(kw)) {
    features.push("image_pack");
  }
  if (/video|tutorial|how\s+to\s+do|demo/i.test(kw)) {
    features.push("video_carousel");
  }

  // Always include knowledge_panel for branded queries
  if (features.length === 0) {
    features.push("featured_snippet", "people_also_ask", "knowledge_panel");
  }

  return [...new Set(features)];
}

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

export function aggregateAiSnippetEligibility(
  serpAnalysis: SerpAnalysis | null,
  crawlData: PageData[],
  firecrawlPages: ContentSample[],
  isLocal = false,
): AiSnippetEligibility | null {
  if (!serpAnalysis || serpAnalysis.positions.length === 0) return null;

  const queries = [
    ...new Set([
      ...serpAnalysis.positions.map((p) => p.keyword),
      ...serpAnalysis.aiOverviewPresence.map((a) => a.keyword),
    ]),
  ].slice(0, 10);

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

  const perQuery: AiSnippetEligibility["perQuery"] = queries.map((query) => {
    const posEntry = serpAnalysis.positions.find(
      (p) => p.keyword.toLowerCase() === query.toLowerCase()
    );
    const brandRanking = posEntry?.position ?? null;

    const serpFeaturesPresent = inferSerpFeaturesForQuery(query, serpAnalysis, isLocal);

    const perFeatureEligibility: SnippetFeatureEligibility[] = serpFeaturesPresent.map((feature) =>
      assessFeatureEligibility(feature, crawlData, firecrawlPages, brandRanking)
    );

    return { query, serpFeaturesPresent, brandRanking, perFeatureEligibility };
  });

  // Aggregate stats
  const totalFeaturesPresent = perQuery.reduce((sum, q) => sum + q.serpFeaturesPresent.length, 0);
  const totalFeaturesEligible = perQuery.reduce(
    (sum, q) => sum + q.perFeatureEligibility.filter((f) => f.currentlyEligible).length,
    0,
  );
  const queriesWithEligibleFeatures = perQuery.filter((q) =>
    q.perFeatureEligibility.some((f) => f.currentlyEligible)
  ).length;
  const eligibilityRatePercent = totalFeaturesPresent > 0
    ? Math.round((totalFeaturesEligible / totalFeaturesPresent) * 100)
    : 0;

  // Top opportunities: rank by easy implementation + high traffic + not yet eligible
  const allOpportunities = perQuery.flatMap((q) =>
    q.perFeatureEligibility
      .filter((f) => !f.currentlyEligible && f.missingFactors.length > 0)
      .map((f) => ({
        query: q.query,
        feature: f.feature,
        difficulty: f.implementationDifficulty,
        traffic: f.estimatedTraffic,
        missingFactors: f.missingFactors,
      }))
  );

  const trafficScore = (t: string) => (t === "high" ? 3 : t === "moderate" ? 2 : 1);
  const diffScore = (d: string) => (d === "easy" ? 3 : d === "moderate" ? 2 : 1);

  const topOpportunities = allOpportunities
    .sort((a, b) => trafficScore(b.traffic) + diffScore(b.difficulty) - trafficScore(a.traffic) - diffScore(a.difficulty))
    .slice(0, 5)
    .map((opp) => ({
      query: opp.query,
      feature: opp.feature,
      rationale: `${opp.feature} is present in search results for this query and represents a ${opp.traffic}-traffic capture opportunity currently unavailable to the brand.`,
      quickestPath: opp.missingFactors[0] ?? "Implement the required schema and content changes",
      estimatedImpact: opp.traffic === "high" ? "High visibility gain — this feature captures significant SERP real estate" : "Moderate visibility gain — incremental traffic and brand presence",
    }));

  return {
    perQuery,
    topOpportunities,
    aggregateStats: {
      totalQueriesAnalyzed: queries.length,
      queriesWithEligibleFeatures,
      totalFeaturesPresent,
      totalFeaturesEligible,
      eligibilityRatePercent,
    },
  };
}
