/**
 * Keyword Research Service
 * ────────────────────────
 * Two layers:
 *   1. Data layer — DataForSEO (real) or Claude estimates (fallback).
 *   2. Strategy layer — Claude analyzes raw data in brand context to
 *      produce topic clusters, quick wins, gap analysis, content plan.
 *
 * 30-day cross-customer cache: same seed + location = serve cached,
 * regardless of which brand originally asked. Brand-specific AI
 * analysis is cached separately per brand.
 */

import { prisma } from "@/lib/prisma";
import { callLlm } from "@/lib/ai-avatar/llm-client";

const DFSEO_LOGIN = process.env.DATAFORSEO_LOGIN ?? "";
const DFSEO_PASS = process.env.DATAFORSEO_PASSWORD ?? "";
const DFSEO_BASE = "https://api.dataforseo.com/v3";
const CACHE_DAYS = 30;

export interface KeywordResult {
  keyword: string;
  searchVolume: number | null;
  difficulty: number | null;
  cpc: number | null;
  competition: string | null;
  intent: string | null;
  trend: number[] | null;
  // Wire-format discriminator — kept generic ("live" vs estimated)
  // so the value never leaks the SERP vendor name into client JSON.
  source: "live" | "ai_estimated";
}

export interface KeywordStrategy {
  summary: string;
  topicClusters: Array<{ name: string; keywords: string[]; contentStrategy: string; priority: string; estimatedTrafficPotential: string }>;
  quickWins: Array<{ keyword: string; currentPosition?: number; volume?: number; action: string }>;
  gapAnalysis: Array<{ keyword: string; volume?: number; difficulty?: number; reason: string }>;
  questionKeywords: Array<{ question: string; volume?: number; suggestedContentType: string }>;
  avoidKeywords: Array<{ keyword: string; reason: string }>;
  contentPlan: Array<{ title: string; targetKeyword: string; relatedKeywords: string[]; intent: string; format: string; priority: string }>;
}

function hasDfseo(): boolean { return !!(DFSEO_LOGIN && DFSEO_PASS); }
function dfseoHeaders(): HeadersInit {
  return { "Content-Type": "application/json", Authorization: `Basic ${Buffer.from(`${DFSEO_LOGIN}:${DFSEO_PASS}`).toString("base64")}` };
}

// ─── Intent classification ──────────────────────────────────────────────────

const INTENT_RE: Array<[RegExp, string]> = [
  [/\b(what|how|why|when|where|which|guide|tips|tutorial|learn|explain)\b/i, "informational"],
  [/\b(buy|price|cost|near me|hire|service|order|book|schedule|quote)\b/i, "transactional"],
  [/\b(best|top|review|compare|vs|versus|alternative|rating)\b/i, "commercial"],
];
export function classifyIntent(kw: string): string {
  for (const [re, intent] of INTENT_RE) if (re.test(kw)) return intent;
  return "informational";
}

// ─── 30-day cross-customer cache ────────────────────────────────────────────

async function getCached(seed: string, location: string): Promise<{ results: KeywordResult[]; aiAnalysis: KeywordStrategy | null; brandId: string } | null> {
  const cutoff = new Date(Date.now() - CACHE_DAYS * 86400000);
  // Cross-customer: ignore brandId in the query — any brand's cached result works
  const cached = await prisma.keywordResearch.findFirst({
    where: { seedKeyword: seed.toLowerCase(), location, createdAt: { gte: cutoff } },
    orderBy: { createdAt: "desc" },
  });
  if (!cached) return null;
  const results = ((cached.results as { keywords?: KeywordResult[] })?.keywords) ?? [];
  const aiAnalysis = cached.aiAnalysis as KeywordStrategy | null;
  return { results, aiAnalysis, brandId: cached.brandId };
}

async function saveCache(brandId: string, seed: string, location: string, language: string, keywords: KeywordResult[], aiAnalysis?: KeywordStrategy) {
  await prisma.keywordResearch.create({
    data: { brandId, seedKeyword: seed.toLowerCase(), location, language, results: { keywords } as object, aiAnalysis: aiAnalysis ? (aiAnalysis as unknown as object) : undefined },
  });
}

// ─── DataForSEO ─────────────────────────────────────────────────────────────

async function dfseoPost(path: string, body: unknown[]): Promise<unknown> {
  const res = await fetch(`${DFSEO_BASE}${path}`, { method: "POST", headers: dfseoHeaders(), body: JSON.stringify(body) });
  if (!res.ok) { console.error(`[keyword-research] DataForSEO ${res.status}`); throw new Error(`DataForSEO ${res.status}`); }
  return res.json();
}

function parseDfseo(item: Record<string, unknown>): KeywordResult {
  const vol = (item.search_volume as number) ?? null;
  const comp = item.competition as string | null;
  const cpc = (item.cpc as number) ?? null;
  const monthly = item.monthly_searches as Array<{ search_volume: number }> | null;
  const difficulty = vol != null && cpc != null ? Math.min(100, Math.round((vol > 10000 ? 40 : vol > 1000 ? 25 : 10) + (cpc > 5 ? 30 : cpc > 1 ? 15 : 5) + (comp === "HIGH" ? 25 : comp === "MEDIUM" ? 15 : 5))) : null;
  return { keyword: String(item.keyword ?? ""), searchVolume: vol, difficulty, cpc, competition: comp?.toLowerCase() ?? null, intent: classifyIntent(String(item.keyword ?? "")), trend: monthly?.map((m) => m.search_volume) ?? null, source: "live" };
}

async function fetchFromDfseo(seed: string, location: string, language: string): Promise<KeywordResult[]> {
  const [volData, relData] = await Promise.all([
    dfseoPost("/keywords_data/google_ads/search_volume/live", [{ keywords: [seed], location_name: location, language_name: language }]),
    dfseoPost("/keywords_data/google_ads/keywords_for_keywords/live", [{ keywords: [seed], location_name: location, language_name: language }]),
  ]) as [{ tasks?: Array<{ result?: Array<Record<string, unknown>> }> }, { tasks?: Array<{ result?: Array<Record<string, unknown>> }> }];
  const vol = (volData.tasks?.[0]?.result ?? []).map(parseDfseo);
  const rel = (relData.tasks?.[0]?.result ?? []).map(parseDfseo).slice(0, 50);
  return [...vol, ...rel];
}

// ─── AI estimation fallback ─────────────────────────────────────────────────

async function estimateFromAI(seed: string, location: string): Promise<KeywordResult[]> {
  const result = await callLlm([
    { role: "system", content: "You are a keyword data estimator. Respond ONLY with JSON." },
    { role: "user", content: `Estimate search data for "${seed}" targeting ${location}. Return: {"keywords":[{"keyword":"...","estimatedVolume":number,"estimatedDifficulty":0-100,"estimatedCpc":number,"intent":"informational|transactional|commercial|navigational","competition":"low|medium|high"}]}. Provide 30 related keywords, 10 question-format, 10 long-tail. Base estimates on real search behavior.` },
  ], () => JSON.stringify({ keywords: [{ keyword: seed, estimatedVolume: null, estimatedDifficulty: 50, estimatedCpc: null, intent: "informational", competition: "medium" }] }), { maxTokens: 4000 });
  try {
    let s = result.reply.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "");
    const f = s.indexOf("{"), l = s.lastIndexOf("}");
    if (f >= 0 && l > f) s = s.slice(f, l + 1);
    const parsed = JSON.parse(s) as { keywords?: Array<Record<string, unknown>> };
    return (parsed.keywords ?? []).map((k) => ({
      keyword: String(k.keyword ?? ""), searchVolume: typeof k.estimatedVolume === "number" ? k.estimatedVolume : null,
      difficulty: typeof k.estimatedDifficulty === "number" ? Math.min(100, k.estimatedDifficulty) : null,
      cpc: typeof k.estimatedCpc === "number" ? k.estimatedCpc : null,
      competition: typeof k.competition === "string" ? k.competition : null,
      intent: typeof k.intent === "string" ? k.intent : classifyIntent(String(k.keyword ?? "")),
      trend: null, source: "ai_estimated" as const,
    }));
  } catch { return []; }
}

// ─── AI strategic analysis ──────────────────────────────────────────────────

export async function analyzeKeywords(keywords: KeywordResult[], brandId: string): Promise<KeywordStrategy> {
  const brand = await prisma.brand.findUnique({ where: { id: brandId }, include: { profile: true } });
  // Fetch current GSC rankings for context
  const gscKws = await prisma.gscQuery.findMany({ where: { brandId }, orderBy: { clicks: "desc" }, take: 30, select: { query: true, clicks: true, position: true } });
  const kwSummary = keywords.slice(0, 30).map((k) => `${k.keyword} (vol:${k.searchVolume ?? "?"}, diff:${k.difficulty ?? "?"}, intent:${k.intent})`).join("\n");
  const gscSummary = gscKws.length ? gscKws.slice(0, 15).map((k) => `${k.query} (pos:${k.position.toFixed(1)}, clicks:${k.clicks})`).join("\n") : "No GSC data available";

  const result = await callLlm([
    { role: "system", content: "You are a senior SEO strategist. Respond ONLY with JSON — no markdown, no commentary." },
    { role: "user", content: `Analyze these keywords for ${brand?.name || "the brand"} (${brand?.industry || "general"}, ${brand?.domain || ""}).

Keyword data:
${kwSummary}

Current rankings (GSC):
${gscSummary}

Services: ${brand?.profile?.services?.join(", ") || "not specified"}
Locations: ${brand?.profile?.locations?.join(", ") || "not specified"}

Provide strategic analysis as JSON:
{"summary":"2-3 sentence overview","topicClusters":[{"name":"...","keywords":["..."],"contentStrategy":"...","priority":"high|medium|low","estimatedTrafficPotential":"X clicks/mo"}],"quickWins":[{"keyword":"...","currentPosition":N,"volume":N,"action":"..."}],"gapAnalysis":[{"keyword":"...","volume":N,"difficulty":N,"reason":"..."}],"questionKeywords":[{"question":"...","volume":N,"suggestedContentType":"FAQ|blog|landing page"}],"avoidKeywords":[{"keyword":"...","reason":"..."}],"contentPlan":[{"title":"...","targetKeyword":"...","relatedKeywords":["..."],"intent":"...","format":"blog|landing|service page","priority":"P1|P2|P3"}]}` },
  ], () => JSON.stringify({ summary: "Configure an AI API key for strategic analysis.", topicClusters: [], quickWins: [], gapAnalysis: [], questionKeywords: [], avoidKeywords: [], contentPlan: [] }), { maxTokens: 6000 });

  try {
    let s = result.reply.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "");
    const f = s.indexOf("{"), l = s.lastIndexOf("}");
    if (f >= 0 && l > f) s = s.slice(f, l + 1);
    return JSON.parse(s) as KeywordStrategy;
  } catch { return { summary: result.reply.slice(0, 500), topicClusters: [], quickWins: [], gapAnalysis: [], questionKeywords: [], avoidKeywords: [], contentPlan: [] }; }
}

// ─── Unified entry points ───────────────────────────────────────────────────

export async function searchAndAnalyze(brandId: string, seed: string, location = "United States", language = "en"): Promise<{ keywords: KeywordResult[]; aiAnalysis: KeywordStrategy; cached: boolean; source: string }> {
  const cached = await getCached(seed, location);
  if (cached?.results?.length && cached.aiAnalysis && cached.brandId === brandId) {
    return { keywords: cached.results, aiAnalysis: cached.aiAnalysis, cached: true, source: cached.results[0]?.source || "ai_estimated" };
  }

  const keywords = cached?.results?.length ? cached.results : (hasDfseo() ? await fetchFromDfseo(seed, location, language) : await estimateFromAI(seed, location));
  const aiAnalysis = await analyzeKeywords(keywords, brandId);
  await saveCache(brandId, seed, location, language, keywords, aiAnalysis);

  return { keywords, aiAnalysis, cached: false, source: keywords[0]?.source || "ai_estimated" };
}

export async function bulkVolume(keywords: string[], location = "United States", language = "en"): Promise<KeywordResult[]> {
  if (hasDfseo()) {
    const data = await dfseoPost("/keywords_data/google_ads/search_volume/live", [{ keywords: keywords.slice(0, 100), location_name: location, language_name: language }]) as { tasks?: Array<{ result?: Array<Record<string, unknown>> }> };
    return (data.tasks?.[0]?.result ?? []).map(parseDfseo);
  }
  const result = await estimateFromAI(keywords.join(", "), location);
  return result;
}
