/**
 * Research Service
 * ─────────────────
 * Performs web-backed research to gather external evidence for analysis.
 * Uses Google Custom Search API for web queries and fetch for page content.
 *
 * All evidence is structured and source-attributed — no fabricated results.
 */

import type {
  ResearchContext, ResearchQuery, ResearchEvidence,
} from "./analysis-schema";

// ─── Configuration ─────────────────────────────────────────────────────────

const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY ?? "";
const GOOGLE_CX = process.env.GOOGLE_SEARCH_CX ?? "";
const MAX_QUERIES = 4;
const MAX_RESULTS_PER_QUERY = 5;
const MAX_PAGE_FETCHES = 3;
const FETCH_TIMEOUT_MS = 8000;

// ─── Types ─────────────────────────────────────────────────────────────────

export interface ResearchRequest {
  domain: string;
  industry?: string;
  period: { start: string; end: string };
  topics: ResearchTopic[];
  uploadedNotes?: string[];
}

export interface ResearchTopic {
  query: string;
  purpose: string;
  sectionMapping: string[];
}

interface SearchResult {
  title: string;
  link: string;
  snippet: string;
  displayLink: string;
}

// ─── Main Entry Point ──────────────────────────────────────────────────────

/**
 * Run external research for an analysis job.
 * Returns structured evidence that gets attached to the analysis.
 */
export async function runResearch(request: ResearchRequest): Promise<ResearchContext> {
  const queries: ResearchQuery[] = [];
  const evidence: ResearchEvidence[] = [];
  let evidenceCounter = 0;

  // Include uploaded notes as evidence
  if (request.uploadedNotes && request.uploadedNotes.length > 0) {
    for (const note of request.uploadedNotes) {
      if (!note.trim()) continue;
      evidenceCounter++;
      evidence.push({
        id: `ev_note_${evidenceCounter}`,
        queryId: "manual",
        sourceType: "manual_note",
        sourceName: "User-provided note",
        sourceUrl: null,
        title: "Manual context",
        snippet: note.trim().slice(0, 500),
        dateCaptured: new Date().toISOString(),
        relevance: "high",
        citationLabel: `[Note ${evidenceCounter}]`,
        sectionMapping: ["findings", "recommendations", "caveats"],
      });
    }
  }

  // Run web searches if API keys are configured
  const canSearch = GOOGLE_API_KEY && GOOGLE_CX;
  const topicsToRun = request.topics.slice(0, MAX_QUERIES);

  for (const topic of topicsToRun) {
    const queryId = `q_${queries.length + 1}`;

    if (!canSearch) {
      queries.push({
        id: queryId,
        query: topic.query,
        purpose: topic.purpose,
        resultCount: 0,
        status: "failed",
        errorMessage: "Web search not configured (missing GOOGLE_API_KEY or GOOGLE_SEARCH_CX)",
      });
      continue;
    }

    try {
      const results = await googleSearch(topic.query);
      queries.push({
        id: queryId,
        query: topic.query,
        purpose: topic.purpose,
        resultCount: results.length,
        status: results.length > 0 ? "completed" : "no_results",
      });

      // Convert search results to evidence
      for (const result of results) {
        evidenceCounter++;
        evidence.push({
          id: `ev_search_${evidenceCounter}`,
          queryId,
          sourceType: "web_search",
          sourceName: result.displayLink,
          sourceUrl: result.link,
          title: result.title,
          snippet: result.snippet,
          dateCaptured: new Date().toISOString(),
          relevance: evidenceCounter <= 2 ? "high" : "medium",
          citationLabel: `[${result.displayLink}]`,
          sectionMapping: topic.sectionMapping,
        });
      }
    } catch (err) {
      queries.push({
        id: queryId,
        query: topic.query,
        purpose: topic.purpose,
        resultCount: 0,
        status: "failed",
        errorMessage: err instanceof Error ? err.message : "Search failed",
      });
    }
  }

  // Deep-fetch top results for richer evidence
  const fetchableEvidence = evidence
    .filter(e => e.sourceType === "web_search" && e.sourceUrl && e.relevance === "high")
    .slice(0, MAX_PAGE_FETCHES);

  for (const ev of fetchableEvidence) {
    if (!ev.sourceUrl) continue;
    try {
      const content = await fetchPageContent(ev.sourceUrl);
      if (content) {
        ev.sourceType = "web_page";
        ev.fullContent = content.slice(0, 2000);
        // Update snippet with richer content if available
        if (content.length > ev.snippet.length) {
          ev.snippet = content.slice(0, 500);
        }
      }
    } catch {
      // Page fetch failed — keep the search snippet as-is
    }
  }

  return {
    queries,
    evidence,
    performedAt: new Date().toISOString(),
    stats: {
      queriesRun: queries.length,
      queriesSucceeded: queries.filter(q => q.status === "completed").length,
      evidenceGathered: evidence.length,
    },
  };
}

// ─── Build Research Topics ─────────────────────────────────────────────────

/**
 * Given analysis context, generate targeted research queries.
 * This produces the research plan — what to search for and why.
 */
export function buildResearchTopics(
  domain: string,
  period: { start: string; end: string },
  analysisType: string,
  industry?: string,
): ResearchTopic[] {
  const year = new Date(period.end).getFullYear();
  const month = new Date(period.end).toLocaleString("en-US", { month: "long" });
  const industryCtx = industry ? ` ${industry}` : "";

  const topics: ResearchTopic[] = [];

  if (analysisType === "seasonal_performance" || analysisType === "custom") {
    topics.push({
      query: `${month} ${year}${industryCtx} SEO trends organic traffic changes`,
      purpose: "Identify seasonal or industry-wide trends that may explain traffic patterns",
      sectionMapping: ["findings", "timeline", "caveats"],
    });
    topics.push({
      query: `Google algorithm update ${month} ${year} search ranking changes`,
      purpose: "Check for recent Google algorithm updates that could affect rankings",
      sectionMapping: ["findings", "timeline", "caveats"],
    });
    topics.push({
      query: `${domain} site:searchengineland.com OR site:searchenginejournal.com OR site:moz.com`,
      purpose: "Find any public mentions or analysis of the client domain",
      sectionMapping: ["findings", "recommendations"],
    });
  }

  if (analysisType === "competitor_gap" || analysisType === "custom") {
    topics.push({
      query: `${domain} competitors${industryCtx} market analysis ${year}`,
      purpose: "Gather competitive landscape context",
      sectionMapping: ["findings", "recommendations"],
    });
  }

  if (analysisType === "technical_health" || analysisType === "custom") {
    topics.push({
      query: `Core Web Vitals best practices ${year} site speed ranking factor`,
      purpose: "Latest technical SEO best practices for health recommendations",
      sectionMapping: ["recommendations"],
    });
  }

  // Always include a market context query
  if (topics.length < MAX_QUERIES) {
    topics.push({
      query: `${industryCtx || "digital marketing"} trends ${month} ${year}`,
      purpose: "General market/industry context for the analysis period",
      sectionMapping: ["timeline", "caveats"],
    });
  }

  return topics.slice(0, MAX_QUERIES);
}

// ─── Google Custom Search ──────────────────────────────────────────────────

async function googleSearch(query: string): Promise<SearchResult[]> {
  const url = new URL("https://www.googleapis.com/customsearch/v1");
  url.searchParams.set("key", GOOGLE_API_KEY);
  url.searchParams.set("cx", GOOGLE_CX);
  url.searchParams.set("q", query);
  url.searchParams.set("num", String(MAX_RESULTS_PER_QUERY));

  const res = await fetch(url.toString(), {
    signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
  });

  if (!res.ok) {
    const text = await res.text().catch(() => "");
    throw new Error(`Google Search API error ${res.status}: ${text.slice(0, 200)}`);
  }

  const data = await res.json();
  const items: SearchResult[] = (data.items ?? []).map((item: Record<string, unknown>) => ({
    title: String(item.title ?? ""),
    link: String(item.link ?? ""),
    snippet: String(item.snippet ?? ""),
    displayLink: String(item.displayLink ?? ""),
  }));

  return items;
}

// ─── Page Content Fetcher ──────────────────────────────────────────────────

async function fetchPageContent(url: string): Promise<string | null> {
  try {
    const res = await fetch(url, {
      headers: {
        "User-Agent": "Mozilla/5.0 (compatible; meSEO-Research/1.0)",
        "Accept": "text/html,application/xhtml+xml",
      },
      signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
      redirect: "follow",
    });

    if (!res.ok) return null;

    const contentType = res.headers.get("content-type") ?? "";
    if (!contentType.includes("text/html") && !contentType.includes("text/plain")) return null;

    const html = await res.text();
    return extractTextFromHtml(html);
  } catch {
    return null;
  }
}

/**
 * Basic HTML → text extraction.
 * Strips tags, scripts, styles, and normalizes whitespace.
 */
function extractTextFromHtml(html: string): string {
  let text = html;
  // Remove script and style blocks
  text = text.replace(/<script[\s\S]*?<\/script>/gi, " ");
  text = text.replace(/<style[\s\S]*?<\/style>/gi, " ");
  text = text.replace(/<nav[\s\S]*?<\/nav>/gi, " ");
  text = text.replace(/<footer[\s\S]*?<\/footer>/gi, " ");
  text = text.replace(/<header[\s\S]*?<\/header>/gi, " ");
  // Remove all remaining HTML tags
  text = text.replace(/<[^>]+>/g, " ");
  // Decode common entities
  text = text.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">")
    .replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, " ");
  // Normalize whitespace
  text = text.replace(/\s+/g, " ").trim();
  return text;
}
